Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调 用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关 系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立 性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。 在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计 应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个 准则就是高内聚低耦合。
这里用数据库连接作为例子讲解。
从上面的例子可以看出,如果采用DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver())方式注册驱动,在没有导入数据库相应的jar包之前,IDEA第一时间会报错,也就是编译时错误,找不到对应的类,这样就造成了编译时依赖。而当我们使用java反射的方式,如Class.forName("com.mysql.cj.jdbc.Driver")注册驱动时,如果没有对应的jar包,是不会在编译时刻出现错误,只会在运行时抛出异常(找不到对应的驱动)。
这样又会产生一个问题,这里咱们采用的是mysql的驱动,如果需要换成oracle的数据库驱动,我们需要在源码中进行修改,这也是很麻烦的。
在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的 方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。 那么,这个读取配置文件,创建和获取三层对象的类就是工厂。
首先是配置文件,这里选择.properties后缀的文件
然后是bean工厂代码的编写,其中javabean的意思就是用java语言编写的可重用组件
工厂的本质就是一个容器,所以我选择使用hashmap作为对象的容器。创建bean对象时可以采用饿汉式或者懒汉式进行对象的创建,看自己的需求而定。可以看到,BeanFactory中创建对象均是采用反射创建对象,避免new关键字造成程序的耦合。
后续创建对象:
通过以上方式,就可以降低程序之间的耦合,而spring正是为我们提供了一个简便的方式降低耦合并且增加了许多功能。
首先了解IOC的概念:
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体(这谁写的,这是什么实体?应该是Core的进程、线程、对象、组件?另外“实体”他的修饰词,内部和外界的划分不应该以创建和被创建为参照物来定义,而是应该以core为界线来定义,这样修饰词就可以称为“内部”),将其所依赖的对象的引用传递(注入)给它。
如果还不怎么理解,这里有一张图帮助理解。
不难看出,上个例子中的BeanFactory就是提供了管理javabean的工厂,创建对象的控制权给到了BeanFactory,而不是用户。
现在学习spring的IOC,第一步导入spring的依赖。
第二步在Resources目录创建后缀为.xml的文件。这里就是spring的配置文件,必须导入配置文件的约束,如下。
第三步让spring管理资源,所以需要在配置文件中配置咱们的service层和dao层的实现类。
第四步测试配置是否成功。这里使用带有main方法的类进行测试,模拟
getbean()的第二个参数为xxx.class,作用是避免强制转换(我懒)。
1、作用:
用于配置对象让 spring 来创建的。 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
2、属性:
3.作用范围以及生命周期
4、实例化Bean的三种方式
第一种:使用默认无参构造函数
其中bean.xml加入以下代码。
第二种:spring 管理实例工厂——使用实例工厂的方法创建对象
instanceFactory.java源代码:
第三种:spring 管理静态工厂——使用静态工厂的方法创建对象
StaticFactory源代码:
1、依赖注入的概念
2、构造函数注入
首先创建一个名为UserTest1的实体类,其中只包含有参构造函数。
bean.xml加入如下代码:
constructor-arg的属性及其用法
3、setter方法注入
首先创建名为UserTest2的实体类,并加入setter方法
bean.xml加入以下代码:
property标签的属性及其用法:
4、注入集合类型
创建CollectionDI实体类
bean.xml加入以下代码
5、实际开发中使用
使用场景:业务层需要调用数据层的方法。
如果我们需要将AccountDao的实现类注入到AccountService的实现类中,可以选择构造方法注入或者setter方法注入,我这里选择setter方法进行注入。
bean.xml文件的编写
这样就能实现在业务层中注入数据层的实现类。
1、在配置文件中开启对注解的支持
2、常用注解
1、导入依赖
这里我采用数据库连接进行一个测试,顺带使用JdbcTemplate操作数据库
2、修改测试类
通过 @RunWith 注解,指定 Spring 的运行器,这里 Spring的运行器是SpringJunit4ClassRunner
通过 @ContextConfiguration 注解,指定 Spring 运行器需要的配置文件路径或者配置类,这里由于我们使用的全注解的方式,所以指定的是配置类。
Test.java源代码:
改造以后就可以在测试类中使用@Autowired等注解注入数据!
3、编写bean.xml配置文件
4、测试
输出结果:
整合成功!
转账的案例:当一个账户向另一个账户进行转账操作时,在转出账户更新之后,抛出异常,则转入账户则不会更新,这违背了事务的一致性(部分代码如下)
分析问题产生的原因:
因为转账方法中,需要获取四个连接,并且每个连接不是相同的,第一步操作就是使用ThreadLocal将线程和连接绑定,保证一个线程中只有一个能控制事务的连接。
第一步,创建ConnectionUtils工具类,实现对线程和连接的绑定,保证只有一个连接。
扩展:
第二步,实现对事务的控制。
第三步、在业务层实现事务控制。
第四步,所有的持久层方法的connection都应该从ConnectionUtils中获取,保证控制事务的连接只有一个
第五步,创建bean.xml实现对注解的支持以及DataSource的配置 ,(简简单单,就不复制过来了)
以上对业务层的改造,能实现对转账方法的事务控制,但是存在一些问题,那就是代码冗余。如果我们需要对业务层的每个方法进行事务控制,就需要写大量的重复代码。况且如果以后修改ConnectionUtils或者TxManager的代码,需要修改很多地方,对日常开发非常的不方便。代理模式能很好的解决此类问题,减少冗余的代码。
代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。
这种代理方式需要代理对象和目标对象实现一样的接口。
优点:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:
冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
举例:
接口类AccountService
接口实现类AccountServiceImpl
静态代理类AccountServiceProxy
注:使用时就要使用静态代理类AccountServiceProxy,而不是接口实现类AccountServiceImpl
动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。
静态代理与动态代理的区别主要在:
静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中
特点:
动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。
java.lang.reflect Proxy,主要方法为
java.lang.reflect InvocationHandler,主要方法为
动态代理对象:ProducerProxy
cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。
cglib特点
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。
如果想代理没有实现接口的类,就可以使用CGLIB实现。
CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。
它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。
cglib与动态代理最大的区别就是
使用动态代理的对象必须实现一个或多个接口
使用cglib代理的对象则无需实现接口,达到代理类无侵入。
使用cglib需要引入cglib的jar包,如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。
cglib的Maven坐标
cglib代理工厂CglibFactory
用于创建AccountServiceImpl的代理对象的工厂
使用xml配置IOC
使用Junit单元测试
<code>Aspect</code>(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
<code>Joint point</code>(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
<code>Pointcut</code>(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
<code>Advice</code>(增强):Advice 定义了在 <code>Pointcut</code> 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
<code>Target</code>(目标对象):织入 <code>Advice</code> 的目标对象。
<code>Weaving</code>(织入):将 <code>Aspect</code> 和其他对象连接起来, 并创建 <code>Adviced</code> object 的过程
其实这些专业术语在学习动态之后就很好理解
导入AOP需要的依赖,aspectjweaver用于解析AOP切入点表达式。
导入xml约束
目标:使用AOP完成动态代理实现的功能,实现对功能的增强
准备一个通知类,用于增强代码。
准备待增强的目标对象,即AOP术语中的<code>target</code>。
基于xml的AOP配置,包括前置通知,后置通知,异常通知,最终通知。
使用Junit进行测试
输出结果
它是spring框架为我们提供的一种可以在代码中<code>手动控制</code>增强方法何时执行的方式。
在Logger类中添加环绕通知的代码
其实配置AOP的环绕通知与自己写的动态代理的代码非常相似。
xml中添加代码
测试结果
在xml中添加约束,开启对注解的支持
使用注解改造Logger类
使用注解完成转账案例时应该在TxManaager中配置。
Spring所有事务代理类都是基于<code>PlatformTransactionManager</code>接口的实现。此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法,如下代码片段。
<code>PlatformTransactionManager</code>包括以下三个操作:
我们在开发中都是使用它的实现类,如下:
导入依赖
编写spring配置文件,完成对事务的控制
使用注解时xml的配置