Spring设计模式总结
简单工厂
BeanFactory:Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参数后创建
还是传入参数前创建这个要根据具体情况来定。
2、FactoryBean:Spring中的FactoryBean接口的bean是一类叫做factory的bean。其特点是:spring会在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这 个bean.getOjbect()方法的返回值。
工厂方法
FactoryBean:,以Bean结尾,表示它是个Bean,它并不是简单的Bean,而是一个能生产对象或者修饰对象的工厂Bean,spring会在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean.getOjbect()方法的返回值。
单例模式
Spring依赖注入Bean实例默认是单例的。Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean里
。getBean中的doGetBean方法底层是调用getSingleton进行bean的创建。保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适配器模式
DispatcherServlet根据HandlerMapping返回的handler,向HandlerAdatper发起请求,处理Handler。
HandlerAdapter根据规则找到对应的Handler并让其执行,执行完毕后Handler会向HandlerAdapter返回一个ModelAndView,最后由HandlerAdapter向DispatchServelet返回一个ModelAndView。
HandlerAdatper使得Handler的扩展变得容易,只需要增加一个新的Handler和一个对应的HandlerAdapter即可。因此Spring定义了一个适配接口,使得每一种Controller有一种对应的 适配器实现类,让适配器代替controller执行相应的方法。这样在扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展 了。
代理模式
AOP底层,就是动态代理模式的实现。 动态代理在内存中构建的,不需要手动编写代理类,需要手工编写代理类,代理类引用被代理对象。
切面在应用运行的时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象创建动态的创建一个代理对象。SpringAOP就是以这种 方式织入切面的。
织入:把切面应用到目标对象并创建新的代理对象的过程。
SpringIOC
但是,我们也知道,当程序运行时,用的是具体的UserService对象、OrderService对象,那这些对象是什么时候创建的? 谁创建的? 包括对象里的属性是什么时候赋的值? 谁赋的? 所有这些都是我们程序员做的,以为我们只是写了类而已,所有的这些都是Spring做的,它才是幕后黑手。
这就是控制:1.控制对象的创建2.控制对象内属性的赋值
如果我们不用Spring,那我们得自己来做这两件事,反过来,我们用Spring,这两件事情就不用我们做了,我们要做的仅仅是定义类,以及定义哪些属性需要Spring来赋值(比如某个属性上加@Autowired),而这其实就是第二个问题的答案,这就是反转,表示—种对象控制权的转移。
那反转有什么用,为什么要反转?
如果我们自己来负责创建对象,自己来给对象中的属性赋值,会出现什么情况?
比如,现在有三个类:1.A类,A类里有一个属性C c;
2.B类,B类里也有一个属性Cc;
3.C类
现在程序要运行,这三个类的对象都需要创建出来,并且相应的属性都需要有值,那么除开定义这三个类之外,我们还得写:
-
A a = new A();
-
B b = new B();
-
C c = new C();
-
a.c = c;
-
b.c = c;
这五行代码是不用Spring的情况下多出来的代码,而且,如果类在多一些,类中的属性在多一些,那相应的代码会更多,而且代码会更复杂。
lOC表示控制反转,表示如果用Spring,那么Spring会负责来创建对象,以及给对象内的属性赋值,也就是如果用Spring,那么对象的控制权会转交给Spring。
单例bean和单例模式
Spring中的事务如何实现
-
Spring事务底层是基于数据库事务和AOP机制的
-
首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean
-
当调用代理对象的方法时,会先判断该方法上是否加了@Transactional注解
-
如果加了,那么则利用事务管理器创建一个数据库连接
-
并且修改数据库连接的autocommit属性为false,禁止此连接的自动提交,这是实现Spring事务非常重要的一步
-
然后执行当前方法,方法中会执行sql
-
执行完当前方法后,如果没有出现异常就直接提交事务
-
如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务
-
Spring事务的隔离级别对应的就是数据库的隔离级别
-
Spring事务的传播机制是Spring事务自己实现的,也是Spring事务中最复杂的
-
Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那么实际上就是先建立一个数据库连接,在此新数据库连接上执行sql
//自定义异常 //1.extends Exception // 1.1 编译期异常,强制需要进行处理 // 1.2 事务不能进行回滚 //2.extends RuntimeException // 2.1 运行期异常,无需强制进行处理 // 2.2 一旦发生运行期异常,事务可以正常回滚
/** * 事务管理 * 1.编程式事务管理 * 1.1 取消事务的自动提交 conn.setAutoCommit(false) * 1.2 执行业务操作 * 1.3 catch块 发生异常时,回滚 conn.rollback() * 1.4 finally块 必执行,提交 conn.commit() 、 conn.setAutoCommit(true) * * 2.声明式事务管理 * @Transcational 对原有的业务逻辑实现增强事务操作 * 原理: * 1.默认使用JDK动态代理、若没有出现接口或者@EnableTransactionManagement(proxyTargetClass = true)属性则转而使用CGLIB动态代理 * 2.基于AOP完成对业务逻辑的事务操作 TransactionAspectSupport事务管理切面 invokeWithinTransaction() 异常通知、返回通知+执行目标方法 */ Spring事务失效的八大场景与原因分析 * 1.数据库不支持事务 事务型存储引擎InnoDB * * 2.方法是private的: Spring事务会基于CGLIB来进行AOP,而CGLIB会基于父子类来失效,子类是代理类,父类 * 是被代理类,如果父类中的某个方法是private的,那么子类就没有办法重写它,也就没有办法额外增加Spring事务的逻辑。 * * 3.方法是final的:原因和private是一样的,也是由于子类不能重写父类中的final的方法 * * 4.异常被吃掉:如果Spring事务没有捕获到异常,那么也就不会回滚了,默认情况下Spring会捕获RuntimeException和Error。 * * 5.类没有被Spring管理 * * 6.单独的线程调用方法:当Mybatis或JdbcTemplate执行SQL时,会从ThreadLocal中去获取数据库连接对象, * 如果开启事务的线程和执行SQL的线程是同一个,那么就能拿到数据库连接对象, * 如果不是同一个线程,那就拿到不到数据库连接对象,这样,Mybatis或JdbcTemplate就会自己去新建一个数据库连接用来执行SQL, * 此数据库连接的autocommit为true,那么执行完SQL就会提交,后续再抛异常也就不能再回滚之前已经提交了的SQL了。 * * 7.没加@Configuration注解:如果用SpringBoot基本没有这个问题,但是如果用的Spring,那么可能会有这个问题, * 这个问题的原因其实也是由于Mybatis或JdbcTemplate会从ThreadLocal中去获取数据库连接, * 但是ThreadLocal中存储的是一个ThreadLocalMap,ThreadLocalMap的key为DataSource对象,value为连接对象,而如果我们没有在ApplicationConfig上添加@Configuration注解的话, * 会导致MAP中存的DataSource对象和Mybatis和JdbcTemplate中的DataSource对象不相等,从而也拿不到数据库连接,导致自己去创建数据库连接了。 * * 8.方法内的自调用: Spring事务是基于AOP的,只要使用代理对象调用某个方法时,Spring事务才能生效, * 而在一个方法中调用使用this.xxx()调用方法时,this并不是代理对象,所以会导致事务失效。 * a.解放办法1:把调用方法拆分到另外一个Bean中 * b.解决办法2:自己注入自己 * c.解决办法3:AopContext.currentProxy()+@EnableAspectJAutoProxy(exposeProxy = true) */