Spring IOC和AOP理解,以及怎么解决循环依赖、用到了哪些设计模式


  • IOC

IOC,控制反转(Inversion of Control),就是把对象的创建(即bean的new操作),交给Spring来实现。

通过XML配置:bean标签是用于配置被spring容器管理的bean信息,我们可以通过bean标签,完成IOC的配置。

  • 使用默认无参构造函数来创建类对象,并存入spring容器
<bean id="userService" class="com.xcj.spring.service.UserServiceImpl"></bean>
public class UserServiceImpl implements UserService {
    
    
}

 

  • 静态工厂方式
<!-- 使用StaticFactory类中的静态方法createDemoService创建对象,并存入spring容器(简单了解下,实际中基本不用) -->
<bean id="demoService" class="com.xcj.spring.factory.StaticFactory" factory-method="createDemoService"></bean>
public class StaticFactory {
    
    public static DemoService createDemoService() {
        
        return new DemoServiceImpl();
    }

}
public class DemoServiceImpl implements DemoService {

    
}

 

  • 实例工厂方式
<!-- 先把工厂的创建交给spring来管理,再在使用工厂的bean来调用里面的方法 -->
<bean id="instanceFactory" class="com.xcj.spring.factory.InstanceFactory"></bean>
<bean id="beanService" factory-bean="instanceFactory" factory-method="createBeanService"></bean>
public class InstanceFactory {

    public static BeanService createBeanService() {
        
        return new BeanServiceImpl();
    }
}
public class BeanServiceImpl implements BeanService {

    
}

 

DI,依赖注入(Dependency Injection),是spring IOC的具体实现,做的工作是Spring实例化bean对象后,对bean对象的属性信息进行赋值的操作。

依赖指的是bean实例中的属性,分为简单类型(8种基本类型和String类型)的属性、POJO类型的属性、集合数组类型的属性。

注入方式:构造器,set方法,注解,(还有使用p名称空间注入数据,本质上还是调用set方法)。

  • 构造器注入
<bean id="userDemoService" class="com.xcj.spring.service.UserDemoServiceImpl">
    <constructor-arg name="id" value="1"></constructor-arg>
    <constructor-arg name="name" value="zhangsan"></constructor-arg>
</bean>
public class UserDemoServiceImpl implements UserDemoService {
    
    private int id;
    
    private String name;
        
    public UserDemoServiceImpl(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

 

  • set方法注入(手动装配,即XML方式)
<bean id="demoSetService" class="com.xcj.spring.service.DemoSetServiceImpl">
    <!-- List类型的属性 -->
    <property name="lists">
        <list>
            <value>11</value>
            <value>22</value>
        </list>
    </property>
    <!-- Set类型的属性 -->
    <property name="sets">
        <set>
            <value>111</value>
            <value>222</value>
        </set>
    </property>
    <!-- Map类型的属性 -->
    <property name="maps">
        <map>
            <entry key="111" value="111-01"/>
            <entry key="222" value="222-01"/>
        </map>
    </property>
    <!-- Properties类型 -->
    <property name="props">
        <props>
            <prop key="name">root</prop>
            <prop key="password">123456</prop>
        </props>
    </property>
</bean>
public class DemoSetServiceImpl implements DemoSetService {

    private List<Object> lists;

    private Set<Object> sets;

    private Map<String, Object> maps;

    private Properties props;

    public void setLists(List<Object> lists) {
        this.lists = lists;
    }

    public void setSets(Set<Object> sets) {
        this.sets = sets;
    }

    public void setMaps(Map<String, Object> maps) {
        this.maps = maps;
    }

    public void setProps(Properties props) {
        this.props = props;
    }

}

 

  • 注解(自动装配)

(1)@Autowired,按类型注入,要唯一;如果遇到相同的,则要加上@Qualifer,表示在类型注入基础上按名称注入。

(2)@Resource,按照bean的name/id注入。

@Service("userService")
public class UserServiceImpl implements UserSetService{
    
    @Autowired
    private UserDao userDao;

}

 

那么问题:Spring怎么解决循环依赖的?通过三级缓存。

循环依赖:两个或多个类之间,形成的互相引用,就是循环依赖。

  • 通过构造方法的循环依赖

构造器的循环依赖,是在bean的实例化new对象时产生的,这里会发生死锁,构造器循环依赖无法解决,只能避免这种方式,尽量在设计时不出现循环依赖的问题,或者改成setter方法。

  • 通过setter方法的循环依赖

setter方法的循环依赖,是在bean的实例化后,对bean属性进行填充时出现。这里需要说明,Spring中Bean需要初始化完成,才能对外使用,创建bean的过程包括实例化、属性填充、初始化、放入singletonObjects集合。

Spring解决setter方法的循环依赖是通过三级缓存解决:

查找单例bean顺序singletonObjects -> earlySingletonObjects ->  singletonFactories:

先从一级缓存singletonObjects查找,如果没有且在创建中,则在二级缓存earlySingletonObjects中查找,还是找不到,且允许循环引用,则进入三级缓存singletonFactories中查找,找到会将其插入二级缓存earlySingletonObjects中,同时将其从三级缓存删除。

添加Bean顺序:

(1)new实例化后,如果一级缓存没有,则bean会存放在三级缓存singletonFactories中,并尝试删除二级缓存中可能存在的bean实例;

(2)new实例化后,如果一级和二级缓存都没有,则在三级缓存中找,找到后会放入二级缓存,并删除三级缓存存放的bean实例或代理对象。

(3)如果bean创建完成,则会直接存放在一级缓存singletonFactories,同时删除二级缓存和三级缓存。

  • singletonObjects:存放完整的Bean实例。

 

  • earlySingletonObjects:存放半成品Bean实例。

 

  • singletonFactories:存放实例对象工厂ObjectFactory。

 

  • AOP

AOP,面向切面编程(Aspect Oriented Programming),是一种编程范式,是OOP(面向对象编程)的延续,采用横向抽取机制,补充了OOP纵向继承体系无法解决的重复代码优化方式。

譬如,假设有A,B有相同的逻辑,我们可以横向抽取相同的部分出来,通过AOP思想和业务串联起来,同时这是可插拔式的。

AOP思想的实现一般都是基于代理模式,给业务代码进行功能增强,将业务代码和系统代码解耦。

如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;

如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类。

动态代理说明,动态代理是在运行期间,针对目标对象进行动态代理。

关于这里涉及到的代理模式,可以看https://www.cnblogs.com/scorpio-cat/p/12715222.html。

(1)使用XML方式实现

public class AopAdvice {
    
    public void saveLog() {
        System.out.println("保存日志");
    }

}
<bean name="aopAdvice" class="com.xcj.spring.advice.AopAdvice"></bean>

<aop:config>
    <aop:aspect ref="aopAdvice">
        <aop:before method="saveLog" pointcut="execution(* com.xcj.spring..*Service.do*(..))" />
    </aop:aspect>
</aop:config>

 

(2)使用注解实现

@Component("aopDemoAdvice")
@Aspect
public class AopDemoAdvice {
    
    @Before(value="execution(* com.xcj.spring..*Service.do*(..))")
    public void saveLog() {
        System.out.println("保存日志");
    }
}
<!-- Spring AOP,通过动态代理技术实现的,而动态代理是基于反射 -->
<!-- 开启注解并扫描指定包中带有注解的类 -->
<context:component-scan base-package="com.xcj.spring.service"></context:component-scan>

<!-- 开启AOP自动代理 -->
<aop:aspectj-autoproxy />

使用纯注解方式,@EnableAspectJAutoproxy代替 <aop:aspectj-autoproxy /> ,@Component-scan代替 <context:component-scan> 

@Configuration
@ComponentScan(basePackages = "com.xcj.spring.service")
@EnableAspectJAutoProxy
public class SpringAOPConfiguration {

    
}

Spring帮我们实现了事务管理的系统代码,我们可以通过配置,将业务代码与事务管理整合在一起。当然,我们也可以自己编写事务管理的代码,然后通过AOP思想组合在一起。

Spring AOP的事务支持:Spring提供了事务管理接口PlatformTransactionManager,JDBC、MyBatis等根据这个接口实现了对应的事务管理器,如DataSourceTransactionManager。

(1)XML方式

public void doSave(User user) {
    
    userDao.save(user);
    
    LogInfo logInfo = new LogInfo();
    logInfo.setContent("新增User:"+user.getName);
    logDao.saveLog(logInfo);
    
}

 

<!-- 事务管理器 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 事务通知,配置事务相关属性:方法、隔离级别、传播行为 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="find*"   read-only="true" />
        <tx:method name="get*"    read-only="true"   propagation="SUPPORTS" />
        <tx:method name="check*"  read-only="true"   propagation="SUPPORTS" />
        <tx:method name="delete*" propagation="REQUIRED"/>
        <tx:method name="do*"     propagation="REQUIRED" />
        <tx:method name="submit" propagation="REQUIRED"/>
        <tx:method name="create" propagation="REQUIRED"/>
        <tx:method name="ido*"    propagation="REQUIRES_NEW" />
        <!-- 必须在事务中运行 -->
    </tx:attributes>
</tx:advice>

<!-- AOP配置 -->
<aop:config proxy-target-class="true">
        <aop:pointcut id="txPointCut" expression="execution(* com.xcj.spring..*Service.*(..))" />
        <aop:advisor id="serviceTx"  pointcut-ref="txPointCut" advice-ref="txAdvice" order="1" />
</aop:config>

 (2)注解方式,XML配置开启事务注解,注解@Transaction。如需使用纯注解,使用@EnableTransactionManagement

<!-- 配置事务管理器 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <property name="dataSource" ref="dataSource" />
</bean>

 

关于AOP的一些问题:

1.什么时候产生代理对象?

IOC容器在创建Bean对象的时候,会触发AOP,产生代理对象并放入IOC容器中。

2.JDK代理方式中,什么时候调用代理对象?

第一次调用目标类接口的实例方法的时候,就会去调用代理对象。

3.有没有可能产生了代理对象,但从没有调用过?

可能。

4.JDK代理方式中,代理对象的处理逻辑是什么?

InvocationHandler的invoke()方法,就是组织增强代码和目标代码的结合。

 5.Spring 用到了哪些设计模式?

  • 简单工厂模式(BeanFactory、ApplicationContext
  • 工厂方法模式(将对象的创建及初始化职责交给工厂对象)
  • 单例模式(单例对象,提供了全局的访问点BeanFactory)
  • 适配器模式(AdvisorAdapter,AOP用到的,BeforeAdvice、AfterAdvice、AfterReturningAdvice,Spring预定义的通知要通过对应的适配器,适配成 MethodInterceptor接口(方法拦截器)类型的对象(如:MethodBeforeAdviceInterceptor 负责适配 MethodBeforeAdvice))
  • 装饰者模式(数据库连接sessionFactory,dataSource)
  • 代理模式(AOP用到的)
  • 策略模式(SimpleInstantiationStrategy,实例化对象的时候用到的)
  • 观察者模式(Spring 事件驱动模型:ApplicationEvent事件、ApplicationListener事件监听者、ApplicationEventPublisher事件发布者)
  • 模板方法模式(JdbcTemplate、hibernateTemplate)


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM