Spring的依赖注入和AOP使用详解

一. 需要的POM.xml依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lqs</groupId>
    <artifactId>spring-aop-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <!-- 进行spring导包 -->
    <dependencies>
        <!--spring的核心包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>
        <!--spring上下文包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>
        <!--spring的aop支持包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>
        <!--spring的测试包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>
        <!--面向切面支持包,Spring要支持AOP必需要导入这个包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.8</version>
        </dependency>
        <!--测试包-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
    </dependencies>

    <build>
        <!--
					下面这段配置是为了测试方便
              maven环境配置文件不能写在java中
              为了做测试方便好看,我想要maven也可以在java文件夹中读取配置
              下面的配置的意思是:在java代码中也读取xml文件
              但是注意:实际开发的时候不允许用这种东西
        -->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/test/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

二. DI依赖注入

1. 构造器注入

1 实体类
@Data
public class MyBean {
    private Long id;
    private String username;
    private YouBean youBean;

    public MyBean(Long id, String username) {
        this.id = id;
        this.username = username;
    }

    public MyBean(Long id, String username, YouBean youBean) {
        this.id = id;
        this.username = username;
        this.youBean = youBean;
    }
}
@Data
public class YouBean {
    private Long id;
    private String username;
}
  1. applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!--  默认使用无参构造 创建对象 -->
    <!--    <bean id="myBean" class="com.lqs.constructor.domain.MyBean2"/>-->

    <!--有参构造-->
    <bean id="myBean" class="com.lqs.constructor.domain.MyBean">
        <!--    索引    -->
        <!--        <constructor-arg index="0" value="100"/>-->
        <!--        <constructor-arg index="1" value="root"/>-->

        <!--属性名赋值-->
        <!--        <constructor-arg name="id" value="10086"/>-->
        <!--        <constructor-arg name="username" value="admin"/>-->

        <!--   通过类型注入     -->
        <!--        <constructor-arg type="java.lang.Long" value="11003"/>-->
        <!--        <constructor-arg type="java.lang.String" value="coderyeah"/>-->

        <constructor-arg name="id" value="10086"/>
        <constructor-arg name="username" value="admin"/>
        <!--        <constructor-arg name="youBean" ref="youBean"/>-->

        <!--内部bean构造器注入,不需要指定ID  但是每次地址值不同-->
        <constructor-arg name="youBean">
            <bean class="com.lqs.constructor.domain.YouBean"/>
        </constructor-arg>
    </bean>

    <bean id="youBean" class="com.lqs.constructor.domain.YouBean"/>
  
  </beans>

2. property-setter注入

2.1 Bean
@Data
@NoArgsConstructor
public class MyBean2 {
    // 简单属性
    private Long id;
    private String name;
    private Boolean sex;
    private BigDecimal salary;
    // 集合属性
    private String[] arrays;
    private List<String> list;
    private List<OtherBean> otherBeanList;
    private Set<String> set;
    private Set<OtherBean> otherBeanSet;
    //	更多用来写配置文件,Alt+回车 导包
	//	老项目,就很有可能会用到它
    private Properties props1;
    private Properties props2;
    }
public class OtherBean {
}
2.2 简单属性
<bean id="myBean2" class="com.lqs.property.domain.MyBean2">
        <property name="id" value="10086"/>
        <property name="name" value="root"/>
        <property name="sex" value="true"/>
        <property name="salary" value="8888.9999"/>
</bean>
2.3 集合属性
<bean id="myBean2" class="com.lqs.property.domain.MyBean2">
<!--     集合属性   -->
        <property name="arrays">
            <array>
                <value>JOJO</value>
                <value>迪奥</value>
                <value>小吴</value>
            </array>
        </property>

        <property name="list">
            <list>
                <value>110</value>
                <value>119</value>
                <value>120</value>
                <value>初音未来</value>
            </list>
        </property>


        <property name="otherBeanList">
            <list>
                <!--         四个对象 三个不同       -->
                <bean class="com.lqs.property.domain.OtherBean"/>
                <bean class="com.lqs.property.domain.OtherBean"/>

                <!--     地址一样           -->
                <ref bean="otherBean"/>
                <ref bean="otherBean"/>
            </list>
        </property>

        <property name="set">
            <set>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
                <value>王五</value>
            </set>
        </property>

        <property name="otherBeanSet">
            <set>
                <bean class="com.lqs.property.domain.OtherBean"/>
                <bean class="com.lqs.property.domain.OtherBean"/>
                <!--     自动去重复           -->
                <ref bean="otherBean"/>
                <ref bean="otherBean"/>
            </set>
        </property>
 </bean>
2.4 properties
<bean id="myBean2" class="com.lqs.property.domain.MyBean2">
<property name="props1">
            <!--     支持中文       -->
            <props>
                <prop key="1">001</prop>
                <prop key="2">002</prop>
                <prop key="特点">自律</prop>
            </props>
        </property>

        <!--    不支持中文    -->
        <property name="props2">
            <value>
                name=admin
                id=007
                sex="true"
            </value>
        </property>
  </bean>

三. AOP

1. 什么是AOP

AOP:全称是Aspect Oriented Programming 即:面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。

2. AOP实现方式
  • 使用动态代理技术。
3. 代理模式
  • SpringAOP底层是使用了代理模式对我们的方法进行增强;
  • 代理模式分类:
  • 静态代理:作用不大,只能代理或增强一个方法,比较鸡肋
  • 动态代理:功能强大,有两种方案,Spring会自动选择使用那种方案
  • 方案一:原生JDK的方式,在我们的类实现了接口之后,就会使用这种方式,spring使用JDK的java.lang.reflect.Proxy类代理,推荐使用,更加解耦
  • 方案二:我如果我们的类没有实现接口,那么Spring使用CGLIB库生成目标对象的子类。

四. aop的xml实现方式

  1. Bean
@Component
public class TxManager {
    public void begin() {
        System.out.println("开启事务......");
    }

    public void commit() {
        System.out.println("提交事务......");
    }

    public void rollback(Throwable e) {
        System.out.println("回滚事务......");
        System.out.println(e.getMessage());
    }

    public void close() {
        System.out.println("关闭连接......");
    }
    
    //环绕通知
    public void around(ProceedingJoinPoint joinPoint) {
        begin();
        try {
            joinPoint.proceed();
            commit();
        } catch (Throwable e) {
            e.printStackTrace();
            rollback(e);
        } finally {
            close();
        }
    }
}
  1. 接口
package com.lqs.aop_xml.service;

public interface IUserService {
    void add();

    void update();

    void delete();
}
  1. 配置
<!--  aop配置  -->
    <aop:config>
        <!--  声明切点
          *:方法返回不限制
          ..:参数不做限制
          expression="execution(* com.lqs.aop_xml.service.IUserService.add(..)
          -->
        <aop:pointcut id="pointcut" expression="execution(* com.lqs.aop_xml.service.IUserService.*(..))"/>
        <!--   配置切面  增强类   -->
        <aop:aspect ref="txManager">
            <!--      在add()方法之前执行begin操作      -->
            <!--            <aop:before method="begin" pointcut-ref="pointcut"/>-->
            <!--     业务方法之后 执行      -->
            <!--            <aop:after-returning method="commit" pointcut-ref="pointcut"/>-->
            <!--发生异常-->
            <!--            <aop:after-throwing method="rollback" pointcut-ref="pointcut" throwing="e"/>-->
            <!--     最后执行       -->
            <!--            <aop:after method="close" pointcut-ref="pointcut"/>-->

            <!--     环绕通知强大 可代替以上通知       -->
            <aop:around method="around" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

五. aop的注解实现方式

  1. 配置注解扫描路径和开启动态代理
<context:component-scan base-package="com.lqs.aop_xml"/>
<context:component-scan base-package="com.lqs.aop_anno"/>
<!--开启AspectJ 自动代理-->
<aop:aspectj-autoproxy/>
  1. 增强类
package com.lqs.aop_anno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect//定义一个切面
public class TxManager2 {
    //定义切点 空的方法体
    @Pointcut("execution(* com.lqs.aop_anno.service.IUserService2.*(..))")
    public void pointCut() {
    }


    //    @Before("pointCut()")
    public void begin() {
        System.out.println("开启事务......");
    }

    //    @AfterReturning("pointCut()")
    public void commit() {
        System.out.println("提交事务......");
    }

    //    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void rollback(Throwable e) {
        System.out.println("回滚事务......");
        System.out.println(e.getMessage());
    }

//        @After("pointCut()")
    public void close() {
        System.out.println("关闭连接......");
    }

    //环绕通知  不与其它通知一起用
    @Around("pointCut()")
    public void around(ProceedingJoinPoint joinPoint) {
        begin();
        try {
            joinPoint.proceed();
            commit();
        } catch (Throwable e) {
            e.printStackTrace();
            rollback(e);
        } finally {
            close();
        }
    }
}

六. spring创建bean的四种方式

  1. 普通方式-通过公共无参进行实例化
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- spring创建bean的四种方式:
            1.以前的方式,在xml中进行配置,或者是注解方式进行扫描
            2.使用静态方法工厂模式:当你要使用的类无法通过new创建出来,而只能通过工厂获取,比如sqlSession
            3.使用普通方法工厂模式
            4.使用FacctoryBean方式
       -->

    <bean id="myBean" class="com.lqs.bean.domain.MyBean"/>

</beans>
  1. 集成静态简单工厂
// 此工厂生产MyBean
public class MyBeanFactory {
    // 此方法是一个静态方法,用来生产MyBean
    public static MyBean getMyBean(){
        return  new MyBean();
    }
}
<!-- 注入的是getMyBean方法的返回值 不是MyFactoryBean  -->
        <bean class="com.lqs.bean.MyFactoryBean" factory-method="getMyBean"/>
  1. 集成实例简单工厂
  • 与第二种的区别就是,当你的工厂方法不是静态方法时,无法通过类名.方法名调用
public class MyFactoryBean {
//    public static MyBean getMyBean() {
//        return new MyBean();
//    }

    public  MyBean getMyBean() {
        return new MyBean();
    }
}
<!--  普通方法创建bean  -->
        <bean id="myFactoryBean" class="com.lqs.bean.MyFactoryBean"/>
     <bean id="myBean" factory-bean="myFactoryBean" factory-method="getMyBean"/>
  1. 使用FactoryBean
package com.lqs.bean;

import com.lqs.bean.domain.MyBean;
import org.springframework.beans.factory.FactoryBean;

// FactoryBean<MyBean>:实现一个FactoryBean接口,并指定要生产的Bean对象
public class MyBeanFactoryBean implements FactoryBean<MyBean> {
  
    // 此方法指定你要生产的bean对象
    public MyBean getObject() throws Exception {
        return new MyBean();
    }
    // 此方法指定你生产的bean的类型,必须指定否则报错
    public Class<?> getObjectType() {
        return MyBean.class;
    }
    // 此方法指定你生产的bean是否是单例的,默认false
    public boolean isSingleton() {
        return true;
    }
}
<!--使用MyBeanFactoryBean方式创建bean-->
    <bean id="myBeanFactoryBean" class="com.lqs.bean.MyBeanFactoryBean"/>

七. FactoryBean和BeanFactory的区别

BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。