Spring模块示意图:
Core Container:核心容器(IOC)
AOP+Aspects:面向切面编程模块
DataAccess/Integration:数据访问模块
Web:Spring开发Web应用模块
控制反转——控制并反转资源的获取方式,由程序员自己创建资源转变为由容器创建和设置资源。
DI: Dependency Injection 依赖注入
spring这个容器中,替你管理着一系列的类,前提是你需要将这些类交给spring容器进行管理,然后在你需要的时候,不是自己去定义,而是直接向spring容器索取,当spring容器知道你的需求之后,就会去它所管理的组件中进行查找,然后直接给你所需要的组件.
id:bean的唯一标识
class:组件的全类名
通过scope属性设置bean的生命周期:
prototype 多实例:
1.在容器启动时不会创建实例,从容器中获取bean时才创建实例
2.每一次获取实例的结果都不一样
singleton 单实例:
1.在容器启动完成之前就创建好bean的实例
2.每一次获取实例的结果都是之前创建的实例
request:
Web环境下,对象与request生命周期一致
session:
Web环境下,对象与session生命周期一致
生命周期属性:
初始化方法:init-method
销毁方法:destroy-method
1.创建一个空的bean:
2.利用静态工厂创建bean:
3.利用实例工厂创建bean:
1.set注入:
基本类型使用value注入,引用类型使用ref注入,默认注入null,若要给有默认值的属性注入null,则使用null标签.
2.使用有参构造器注入:
在bean中定义一个有参构造函数.
public Person(String name, Integer age, String gender, String email) {
this.name = name;
this.age = age;
this.gender = gender;
this.email = email;
System.out.println("调用有参构造器...");
}
配置文件:
3.名称空间注入
引入p名称空间:在beans标签中加入
xmlns:p="http://www.springframework.org/schema/p"
4.SPEL注入:
5.注入list:
6.注入map:
使用utils名称空间注入:
引入utils名称空间
xmlns:utils="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd">
定义一个map:
定义bean:
7.注入properties:
root
123456
8.注入级联属性:
9.加载外部配置文件:
使用注解将组件快速的加入到容器中需要:
spring有四个注解:
@Controller 控制器 给控制器(servlet)层的组件加
@Service 业务逻辑
@Repository 给数据库层(持久化,Dao层)的组件添加这个注解
@Component 给不属于以上几层的组件添加这个注解
注解可以随便加,spring底层不会验证组件是否如注解所说.
1.组件的id默认就是组件的类名首字母小写,设置组件id在注解后加括号,括号中设置id.
@Repository(“id”)
2.组件的作用域,默认是单例的
@Scope(value=“prototype”) //设置为多实例
context:component-scan:自动组件扫描
base-package:指定扫描的基础包,spring会将基础包及下面所有包中加了注解的类自动扫描进ioc容器
//自动装配,自动的为这个属性赋值
//@Qualifier:指定一个名字作为id,让spring别使用变量名作为id.
@Qualifier("bookService")
//@Autowired(required = false) 找不到就装配null
@Autowired
private BookService bookServiceExt2;
在方法参数上添加@Qualifer注解:
/**
* 方法上有autowired,这个方法的每一个参数都会注入值,且会在创建bean时自动运行
*/
@Autowired
public void hahaha(BookDao bookDao, @Qualifier("bookService")BookService bookService){
System.out.println("Spring运行了这个方法..." + bookDao + "," + bookService);
}
使用注解加入到容器中的组件,和使用配置加入到容器中的组件行为是默认一样的
@RunWith(SpringJUnit4ClassRunner.class)//spring测试包
@ContextConfiguration(“classpath:applicationContext.xml”) //配置文件的地址
面向切面编程:指在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行
JoinPoint(连接点):目标对象中,所有可以增强的方法,就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。
Pointcut(切入点):目标对象中,已经被增强的方法。调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法。
Advice(通知/增强) :增强方法的代码、想要的功能。
Target(目标对象):被代理对象,被通知的对象,被增强的类对象。
Weaving(织入):将通知应用到连接点形成切入点的过程
Proxy(代理):将通知织入到目标对象之后形成的代理对象
aspect(切面):切入点+通知————通知(Advice)说明了干什么的内容(即方法体代码)和什么时候干(什么时候通过方法名中的before,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),切点表达式等定义。
1.导包
spring-aop + spring-aspects + springsource.org.aopalliance + springsource.org.aspectj.weaver
2.写配置
1,将目标类和切面类(封装了通知方法(在目标方法执行前后执行的方法))加入到ioc容器中
2.告诉Spring哪个是切面类
3.告诉Spring,切面类里面的每一个方法都是何时何地运行
4.开启基于注解的AOP功能(或使用配置文件)
3.测试
重要的用配置,不重要的用注解
先引入AOP名称空间,再加上下面代码:
开启使用注解。
使用@Aspect注解可以将切面类加载入ioc容器
使用@Order(Integer)注解可以改变切面加载顺序,数值越小越优先
5个通知注解:
前置通知:@Before():在目标方法之前运行
后置通知:@After():在目标方法结束之后运行
返回通知:@AfterReturning():在目标方法正常返回之后
异常通知:@AfterThrowing():在目标方法抛出异常之后运行
环绕通知:@Around():围绕方法执行
try{
//@Before
method.invoke();
//@AfterReturning
}catch(e){
//@AfterThrowing
}finally{
//@After
}
上述5个注解在()内设置value值可指定切点位置
/**
* 方法切入点表达式:execution(访问权限符 返回值类型 方法签名)
* 支持通配符:
* *:
* 1.匹配一个或多个字符:execution(public int com.third.impl.MyMathCalculator.*(int,int))
* 2.匹配任意一个参数:
* 3.只能匹配一层路径
* 4.权限位置不能用*号表示,想要表示任意权限,不写就行
* ..:
* 1.匹配任意多个参数和任意类型参数
* 2.匹配任意多层路径
* 支持&&,||,!运算
*/
/**
* 抽取可重用的切入点表达式:
* 1.随便声明一个没有实现的返回void的方法
* 2.给方法上标注@Pointcut注解
*/
@Pointcut("execution(public int com.third.impl.MyMathCalculator.*(int,int))")
public void hahaMyPoint(){};
使用@AfterReturning注解中的returning属性可以获取方法调用后的返回值.
eg:
/**
* 使用AfterReturning注解中的returing来接收返回值,returing = 接收变量名称
* @param joinPoint 连接点
* @param result 返回值
*/
@AfterReturning(value = "hahaMyPoint()",returning = "result")
public static void logReturn(JoinPoint joinPoint,Object result){
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println("[Logs][" + name +"]方法正常执行完成,结果为[" + result + "]");
}
/**
* 在通知方法运行时,拿到目标方法的详细信息
* 1.给通知方法的参数列表上添加JoinPoint参数:
* 2.使用throwing告诉spring接收异常的变量
* 3.Exception尽量往大了写,写小了便只能接收那一类的异常
*/
@AfterThrowing(value = "hahaMyPoint()",throwing = "e")
public static void logException(JoinPoint joinPoint, Exception e){
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println("[Logs][" + name + "]方法出现异常,异常信息是:" + e.getMessage());
}
环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。
eg:
@Around("hahaMyPoint()")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs();
Object proceed = null;
try {
System.out.println("方法前置通知");
//就是利用反射调用目标方法
proceed = pjp.proceed(args);
System.out.println("方法正常执行通知");
} catch (Exception e){
System.out.println("方法异常执行通知");
e.printStackTrace();
throw new RuntimeException(e);//不加这行外界感受不到
} finally {
System.out.println("方法结束执行通知");
}
return proceed;
}
在使用除环绕通知外的其他四个通知时,方法的输出如下:
/**
* 通知方法的执行顺序:
* 正常执行:@Before(前置通知) =====> @After(后置通知) =========> @AfterReturning(正常返回)
* 异常执行:@Before(前置通知) =====> @After(后置通知) =========> @AfterThrowing(方法执行异常)
*/
[Logs][add]方法开始执行,用的参数列表[[2, 13]]
[Logs][add]执行完成
[Logs][add]方法正常执行完成,结果为[15]
使用环绕通知时,方法的输出如下:
方法前置通知
方法正常执行通知
方法结束执行通知
15
环绕通知优于其他通知执行。
顺序:
(环绕前置->普通前置)->目标方法执行->环绕正常返回/异常执行->环绕后置->普通后置->普通返回或异常
有时可以普通前置在前面执行。
对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。
与栈的顺序一样,先进后出
环绕通知只影响当前切面。
在bean配置文件中,所有的Spring AOP配置都必须定义在
元素内部。对于每个切面而言,都要创建一个
元素来为具体的切面实现引用后端bean实例。
eg:
事务部分待补充
To be continue…
1.https://blog.csdn.net/itcats_cn/article/details/81479185
2.https://www.bilibili.com/video/av71336532/