Spring框架学习笔记

  Spring概念

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

  Spring体系结构

核心容器(Core Container),数据访问/集成(Data Access/Integration)层,Web层,AOP(Aspect Oriented Programming)模块,植入(Instrumentation)模块,消息传输(Messaging),测试(Test)模块。

   IOC概念与背景

 程序的耦合

耦合:程序间的依赖关系,包括类之间的依赖与方法间的依赖

解耦:

1.使用反射来创建对象,而避免使用new关键字

2.通过读取配置文件来获取要创建的对象全限定类名

普通程序中的耦合举例:(耦合现象严重)

持久层dao接口:提供保存账户方法

持久层dao实现类:实现保存账户的方法

业务层service接口:提供保存账户方法

业务层service实现类:创建持久层实现类的对象,由该对象实现保存账户的方法

模拟表现层client:在main函数中创建业务层实现类的对象,由该对象调用业务层实现类中的保存账户方法

  解耦的思想

思想概述:

把三层(dao,service,client)的对象都使用配置文件配置起来。

当启动服务器应用加载的时候,让一个工厂类中的方法通过读取配置文件,把这些对象创建出来并保存到一个map容器中。

在接下来的使用的时候,直接使用创建好的对象即可。

创建Bean对象的工厂:

baen在计算机英语中,是可重用组件的含义

javabean是用java语言编写的可重用组件。

正式介绍Ioc

全称为:inverse of control

是Spring框架的重要特征。把创建对象的权力交给框架。常见的方式有:依赖注入,依赖查找。

意义:削减耦合。

  基于xml配置的ioc程序实现

步骤1:新建一个maven工程

步骤2:在弹出来的pom.xml文件中写packaging标签,并导入springframework依赖

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6     <groupId>org.example</groupId>
 7     <artifactId>springIoc</artifactId>
 8     <version>1.0-SNAPSHOT</version>
 9     <packaging>jar</packaging>
10     <dependencies>
11         <dependency>
12             <groupId>org.springframework</groupId>
13             <artifactId>spring-context</artifactId>
14             <version>5.0.2.RELEASE</version>
15         </dependency>
16     </dependencies>
17 </project>
View Code

步骤3:在main/resources包下创建一个bean.xml配置文件,下面开始配置:

打开Spring Framework Documentation网页,点core,按ctrl+f,搜索xmlns,复制配置头到配置文件中,并补全标签尾部。在其中写两个bean,分别包含service和dao的id与class。

1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4        xsi:schemaLocation="http://www.springframework.org/schema/beans
5         http://www.springframework.org/schema/beans/spring-beans.xsd">
6     <bean id="accountService" class="service.impl.AccountServiceImpl"></bean>
7     <bean id="accountDao" class="dao.impl.AccountDaoImpl"></bean>
8 </beans>
View Code

步骤4:分别写好dao层接口,实现类,service层接口,实现类。

dao层接口:定义一个saveAccount()方法即可。

1 package dao;
2 
3 public interface IAccountDao {
4     void saveAccount();
5 }
View Code

dao层实现类:实现dao接口,对dao层接口中的方法进行实现即可。

 1 package dao.impl;
 2 
 3 import dao.IAccountDao;
 4 
 5 public class AccountDaoImpl implements IAccountDao {
 6 
 7     public void saveAccount() {
 8         System.out.println("保存了账户");
 9     }
10 }
View Code

service层接口:定义一个saveAccount()方法即可。

1 package service;
2 
3 public interface IAccountService {
4     void saveAccount();
5 }
View Code

service层实现类:实现service接口,创建一个dao接口的对象,写一个构造方法(用来指示service层对象的创建),对seivice层接口中的方法进行实现。

 1 package service.impl;
 2 
 3 import dao.IAccountDao;
 4 import service.IAccountService;
 5 
 6 public class AccountServiceImpl implements IAccountService {
 7     private IAccountDao accountDao ;
 8     public AccountServiceImpl(){
 9         System.out.println("对象创建了");
10     }
11     public void saveAccount() {
12         accountDao.saveAccount();
13     }
14 }
View Code

步骤5:创建一个表现层的类Client,其中进行如下操作:

用ApplicationContext接口创建一个ClassPathXmlApplicationContext类的Spring核心容器,参数传入bean.xml配置文件,这个容器用来存放创建好的对象。

根据id获取dao层与service层的对象,可打印查看。

 1 package ui;
 2 
 3 import dao.IAccountDao;
 4 
 5 import org.springframework.context.ApplicationContext;
 6 import org.springframework.context.support.ClassPathXmlApplicationContext;
 7 import service.IAccountService;
 8 
 9 public class Client {
10     public static void main(String[] args) {
11         //获取Spring核心容器的对象ac
12         ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
13         //根据id获取bean对象,这两行用了两种方法
14         IAccountService as = (IAccountService)ac.getBean("accountService");
15         IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
16         System.out.println(as);
17         System.out.println(adao);
18     }
19 }
View Code

  补充一些知识:

ApplicationContext 接口的实现类:
ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件 推荐使用这种
FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建spring 容器。它用来读取注解。

关于Spring核心容器的两个接口:

BeanFactory 才是Spring 容器中的顶层接口。
ApplicationContext 是它的子接口。
ApplicationContext:立即加载。只要一读取配置文件,默认情况下就会创建对象。
BeanFactory:延迟加载。什么使用什么时候创建对象。

在bean.xml种创建bean的3种方式:

1.使用默认构造函数创建,要求对应的类中必须有默认的构造函数,否则必须用到constructor-arg标签(在依赖注入中详细说)

格式:<bean id="accountService" class="service.impl.AccountServiceImpl"></bean>

2.使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入Spring容器)

格式:<bean id="instanceFactory" class="factory.instanceFactory"></bean>

           <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

3.使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入Spring容器)

格式:<bean id="accountService" class="factory.StaticFactory" factory-method="getAccountService"></bean>

bean的作用范围

单例:所有请求使用同一个对象

多例:不同的请求用不同的对象

Spring中默认为单例,可以用bean标签的scope属性来设置

其中singleton为单例,prototype为多例。

bean的生命周期

单例对象:与Spring核心容器共存亡

多例对象:使用时由Spring创建,直到被垃圾回收机制回收时死亡。

  Spring中的依赖注入

Dependency Injection

依赖关系:在当前类中需要用到其他类的对象

依赖注入:依赖关系的维护,就是前面我们在bean.xml文件中配置bean的过程。

Spring如何管理:在当前类中需要用到其他类的对象时,交由Spring来提供,我们只需要在配置文件中说明即可。

能注入的数据包括:基本数据类型,bean类型,复杂/集合类型。

注入方式1:通过构造函数注入

事先创建好需要注入的类,一般为service实现类和dao实现类。如果需要注入基本类型数据,则事先定义好私有属性。

在bean标签内部使用constructor-arg标签。

标签属性:type用于指明要注入的数据的数据类型。index指构造函数中的索引位置,注入第几个数据,从0开始。name指数据名称(一般用name)。value用于赋值。ref用于指向别的bean的id名称。

例:<bean id="accountService" class="service.impl.AccountServiceImpl">

<constructor-arg name=xxx value="xxx"></constructor-arg>

    </bean>

注入方式2:set方法注入(常用)

事先生成属性的set方法。

在bean标签内部使用property标签。

标签属性与方式1相同。需要注意,name只看set方法的名字。

注入集合时,指定name属性后,再选择相应的子标签array,set等,再使用value或ref属性。

特别地:注入map时,须先写明map子标签,再写entry标签,然后写key与value。或写props子标签,写好key后,props标签体的内容默认为value。

  AOP概念与背景

问题导入:

以客户增删改查程序为例:

版本1:service实现类中,我们创建了一个dao层对象,并为其定义了一系列的增删改查方法。

存在问题:这里没有手动设置自动提交事务,默认为true,即自动提交事务。一旦方法中出现异常,就可能会不满足事务的一致性。

版本2:另外写一个TransactionManager类,定义一个DBAssit对象。并写好开启事务,提交事务,事务回滚,释放资源的方法。然后修改原service实现类中的增删改查方法:在try中先开启事务,最后提交事务,在catch中回滚事务,在finally中释放资源。

存在问题:业务操作得到了实现。但是业务层方法变得臃肿了,重复代码多。并且业务层方法和事务控制方法耦合了。如果我们此时提交,回滚,释放资源中任何一个方法名变更,都需要修改业务层的代码。

版本3:使用动态代理。

动态代理的特点:字节码随用随创建,随用随加载。

动态代理常用的两种方式 :

基于接口的动态代理:JDK官方的Proxy类

与三大类中平行建一个factory包,其中建一个beanfactory类。

该类中的核心操作:

1.创建service层的对象(被代理对象)

2.创建代理对象

3.在InvocationHandler()中写一个方法,这样执行被代理(service)对象的任何方法时,都会先执行它。

4.在该方法中写上基于TransactionManager类的开启事务,提交事务,事务回滚,释放资源方法。

基于子类的动态代理 提供者:第三方的CGLib,需要导入asm.jar。(略)

而aop,就是通过配置的方式,实现这个功能!

  正式介绍aop

 AOP:全称是Aspect Oriented Programming即:面向切面编程。

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

一些专业术语:

Joinpoint(连接点): 指被拦截到的点。在spring中,这些点指service层接口中的方法。

Pointcut(切入点): 指我们要对哪些Joinpoint进行拦截的定义(有些连接点被指定不被拦截,那就不是切入点)。

Advice(通知/增强): 拦截到Joinpoint之后所要做的事情就是通知。

在通知代码中,我们总会执行method.invoke()方法(调用切入点方法),所以我们根据通知语句的位置,分为前置通知(invoke方法前的语句),后置通知(invoke方法后的语句),异常通知(catch语句中),最终通知(finally语句中),环绕通知(整体)

Target(目标对象): 被代理对象,这里就是service层的对象

Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。 

Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类。

Aspect(切面): 是切入点和通知(引介)的结合。(把通知配置出来就是切面)

aop编程的核心:

a、开发阶段

1.编写核心业务代码(开发主线),

2.把公用代码抽取出来,制作成通知。

3.在配置文件中,声明切入点与通知间的关系,即切面。

b、运行阶段(Spring框架完成)

Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

  基于xml配置的aop程序实现

步骤1新建一个maven工程

步骤2:在弹出来的pom.xml文件中写packaging标签,并导入springframework依赖和aspectj依赖

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <groupId>org.example</groupId>
 8     <artifactId>springAop</artifactId>
 9     <version>1.0-SNAPSHOT</version>
10     <packaging>jar</packaging>
11 
12     <dependencies>
13         <dependency>
14             <groupId>org.springframework</groupId>
15             <artifactId>spring-context</artifactId>
16             <version>5.0.2.RELEASE</version>
17         </dependency>
18 
19         <dependency>
20         <groupId>org.aspectj</groupId>
21         <artifactId>aspectjweaver</artifactId>
22         <version>1.8.7</version>
23     </dependency>
24     </dependencies>
25     
26 </project>
View Code

步骤3:在main/resources包下创建一个bean.xml配置文件,下面开始配置:

打开Spring Framework Documentation网页,点core,按ctrl+f,搜索xmlns:aop,复制配置头到配置文件中,并补全标签尾部。在其中写两个bean(ioc与aop的配置),分别包含service和logger的id与class。

在aop配置中分别对5种类型的通知进行配置。(注意:环绕通知运行与其他四种不能同时存在)

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xmlns:aop="http://www.springframework.org/schema/aop"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans
 6         http://www.springframework.org/schema/beans/spring-beans.xsd
 7         http://www.springframework.org/schema/aop
 8         http://www.springframework.org/schema/aop/spring-aop.xsd">
 9     <!--ioc配置-->
10     <bean id="accountService" class="service.impl.AccountServiceImpl"></bean>
11     <!--aop配置-->
12     <bean id="logger" class="utils.Logger"></bean>
13     <aop:config>
14         <!--配置切入点表达式,id为标识,expression为内容-->
15         <aop:pointcut id="pt1" expression="execution(* service..impl.*.*(..))"></aop:pointcut>
16         <!--配置切面-->
17         <aop:aspect id="logAdvice" ref="logger">
18             <!--配置通知类型,并且建立通知方法和切入点方法的关联-->
19             <!--前置通知-->
20             <aop:before method="BeforePrintLog" pointcut-ref="pt1"></aop:before>
21             <!--后置通知-->
22             <aop:after-returning method="AfterPrintLog" pointcut-ref="pt1"></aop:after-returning>
23             <!--异常通知-->
24             <aop:after-throwing method="ThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>
25             <!--最终通知-->
26             <aop:after method="FinalPrintLog" pointcut-ref="pt1"></aop:after>
27             <!--环绕通知-->
28             <!--aop:around method="AroundPrintLog" pointcut-ref="pt1"></aop:around-->
29         </aop:aspect>
30     </aop:config>
31 </beans>
View Code

步骤4:main/java/service下分别写好service层接口,实现类。

service层接口:定义几个方法,这里尝试了三种不同类型的方法(有无参数,有无返回值)。

1 package service;
2 
3 public interface IAccountService {
4     void saveAccount();
5     void updateAccount(int i);
6     int deleteAccount();
7 
8 }
View Code

service层实现类:实现service接口,对seivice层接口中的方法进行实现。

 1 package service.impl;
 2 
 3 import service.IAccountService;
 4 
 5 public class AccountServiceImpl implements IAccountService {
 6     public void saveAccount() {
 7         System.out.println("执行了保存");
 8     }
 9 
10     public void updateAccount(int i) {
11         System.out.println("执行了更新"+i);
12     }
13 
14     public int deleteAccount() {
15         System.out.println("执行了删除");
16         return 0;
17     }
18 }
View Code

步骤5:main/java下创建一个utils包,其中创建一个logger类,其中写好5种通知的方法体。

 1 package utils;
 2 
 3 import org.aspectj.lang.ProceedingJoinPoint;
 4 
 5 public class Logger {
 6     //用于打印日志,在切入点方法之前执行
 7     public void BeforePrintLog(){
 8         System.out.println("前置通知");
 9     }
10     public void AfterPrintLog(){
11         System.out.println("后置通知");
12     }
13     public void ThrowingPrintLog(){
14         System.out.println("异常通知");
15     }
16     public void FinalPrintLog(){
17         System.out.println("最终通知");
18     }
19     public Object AroundPrintLog(ProceedingJoinPoint pjp) {
20         Object rtValue = null;
21         try {
22             Object[] args = pjp.getArgs();
23             System.out.println("环绕通知-前置");
24             rtValue = pjp.proceed(args);
25             System.out.println("环绕通知-后置");
26             return rtValue;
27         } catch (Throwable t) {
28             System.out.println("环绕通知-异常");
29             throw new RuntimeException(t);
30         } finally {
31             System.out.println("环绕通知-最终");
32         }
33     }
34 }
View Code

步骤6:test/java/test下创建一个AOPTest类。

用ApplicationContext接口创建一个ClassPathXmlApplicationContext类的Spring核心容器,参数传入bean.xml配置文件,这个容器用来存放创建好的对象。

用该容器对象调用service实现类种的方法。

 1 package test;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 import service.IAccountService;
 6 
 7 import java.sql.SQLOutput;
 8 
 9 public class AOPTest {
10     public static void main(String[] args) {
11         //获取Spring核心容器的对象ac
12         ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
13         IAccountService as = (IAccountService)ac.getBean("accountService");
14         as.saveAccount();
15         System.out.println("------------------");
16         as.deleteAccount();
17         System.out.println("------------------");
18         as.updateAccount(1);
19         System.out.println("------------------");
20     }
21 }
View Code

 

posted @ 2020-05-07 13:19  菅兮徽音  阅读(258)  评论(0编辑  收藏  举报