1、spring核心技术
spring出现在2002年左右,主要用于减轻对项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系。spring的核心技术是ioc,aop。能实现模块之间,类之间的解耦合。
IOC:
ioc(inversion of control 控制反转):把对象的创建、赋值、管理工作都交给代码之外的容器实现,也就是对象的创建都是有其他外部资源完成。
控制:创建对象,对象的属性赋值,对象之间的关系管理。
反转:把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现,由容器代替开发人员管理对象、创建对象、给对象赋值。
正转:由开发人员在代码中,使用new构造方法创建对象,开发人员主动管理对象。
容器:是一个服务器软件,一个框架(spring)。
ioc的体现:和servlet原理一样
1:创建类继承HttpServlet
2:在web.xml中注册servlet,使用
<servlet-name>myservlet</servlet-name>
<servlet-class>com.fd.controller.MyServlet</servlet-class>
3:没有创建Servlet对象,没有使用MyServlet myservlet=new MyServlet();
4:Servlet是Tomcat服务器,它能帮你创建对象。Tomcat也称为容器。Tomcat作为容器,里面存放的有Servlet对象,Filter、Listener对象。
ioc的技术实现:DI(Dependency injection)依赖注入是ioc的技术实现。使用者只需要在程序中提供要使用的对象名称就行,至于赋值、查找,都可以交由容器内部实现。
spring是使用的DI实现了ioc的功能,spring底层创建对象,使用的是反射机制。
**AOP:**
1.动态代理
jdk动态代理:使用jdk中的Proxy,Method,InvocationHandler创建代理对象。jdk动态代理要求目标类必须实现接口。使用方式如3.5、动态代理
cglib动态代理:第三方的工具库,创建代理对象,原理是继承。通过继承目标类,创建子类。子类就是代理对象。要求目标类不能是final的,方法也不能是final的。
2.动态代理的作用:
1)在目标类源代码不改变的情况下,增加功能
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务和日志,事务非业务功能分离
3.Aop(Aspect Orient Programming):
Aspect: 切面,给你的目标类增加功能就是切面。像日志、事务都是切面。
切面的特点:一般都是非业务的方法,独立使用的
Orient: 面向
Programming: 编程
Aop(Aspect Orient Programming)面向切面编程,基于动态代理的,可以使用jdk,cglib两种方式。
Aop就是动态代理的规范化,把动态代理的实现步骤、方式都定义好了,让开发人员用一种统一的方式使用动态代理。
术语:
1)Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能。非业务功能,常见的切面功能有日志,事务,统计信息,参数检查,权限验证。
2)JoinPoint:连接点,连接业务方法和切面的位置。就是某个类中的业务方法。
3)PointCut:切入点,指多个连接点方法的集合。多个方法。
4)目标对象:给哪个类的方法增加功能,这个类就是目标对象。
5)Advice:通知,通知表示切面功能执行的时间。
切面功能的三个关键要素:
1)切面的功能代码,切面是用来干什么的。
2)切面的执行位置,使用PointCut表示切面执行的位置。
3)切面的执行时间,使用Advice表示时间,在目标方法之前还是在目标方法之后。
2、spring配置文件相关标签及使用
在使用spring之前,需要先添加依赖
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2.1、beans根标签
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
spring的配置文件
1.beans:是根标签,spring把java对象称为bean
2.spring-beans.xsd是约束文件,和mybatis中dtd是一样的
-->
</beans>
2.2、bean对象
<!--
声明bean,就是告诉spring要创建某个类的对象
id:对象的自定义名称,唯一值,spring通过这个名称找到对象。
class:类的全限定名称(不能是接口,因为spring使用反射机制创建对象,必须用类)
autowire:自动装载,分别有byName和byType可选,用于引用属性的自动注入,详情看3.3.1
spring框架有一个map用来存放对象,在bean配置好后
使用spring创建对象相当于 springMap.put("someService",new SomeServiceImpl());
-->
<bean id="someService" class="com.wxk.service.SomeServiceImpl"/>
<!--spring也能创建非自定义的类的对象-->
<bean id="myDate" class="java.util.Date"/>
2.3、property属性(set注入)
一个property只能给一个属性赋值,赋值仅仅只是调用的set方法,不管set方法中是否是赋值
如name=“age” 则实际是spring创建对象后调用了该类中的setAge方法
<bean id="xxx" class="xxx">
<property name="属性名字" value="该属性的值"/>
<property ...../>
</bean>
2.4、constructor-arg(构造注入)
<!--
构造注入:spring调用类的有参构造方法,在创建对象的同时,在构造方法中给属性赋值
构造注入使用<constructor-arg>标签
<constructor-arg>标签:一个<constructor-arg>标签只能表示构造方法的一个参数,因此该标签的数量应和想要赋值的属性数量相同
<constructor-arg>标签属性:
name:表示构造方法的形参名
index:表示构造方法的参数的位置,参数从左到右位置是0,1,2……的顺序
value:构造方法的形参是简单类型的,使用value
ref:构造方法的形参是引用类型的,使用ref
-->
<bean id="xxx" class="xxx">
<constructor-arg name="name" index="0" value="李四"/>
<constructor-arg name="age" index="1" value="21"/>
<constructor-arg name="school" index="2" ref="mySchool"/>
</bean>
2.5、import导入
<!--
包含关系的配置文件:total.xml表示spring主配置文件,主配置文件包含其他配置文件,且主配置文件一般是不定义对象的。
语法:
<import resource="classpath:其他配置文件的路径"/>
关键字"classpath":表示类路径(class文件所在的目录,即编译生成的target目录下的classes文件)
在spring配置文件中要指定其他文件的位置,需要用classpath,告诉spring到哪去加载读取。
-->
<!--加载的是文件列表-->
<import resource="classpath:spring-school.xml"/>
<import resource="classpath:spring-student.xml"/>
除了输入完整的路径classpath同样可以用通配符表示
<!--
在包含关系的配置文件中,可以用通配符(*:表示任意字符)
注意:主配置文件不能在通配符的范围之内,否则会形成死循环
-->
<import resource="classpath:spring-*.xml"/>
2.6、component-scan组件扫描器
<!--
声明组件扫描器(component-scan),组件就是java对象
base-package:指定注解在你项目中的包名
<component-scan>工作方式:spring会扫描遍历base-package指定的包,
扫描该包中和其子包中所有的类,找到类中的注解,按照注解的功能创造对象,或给属性赋值
-->
<context:component-scan base-package="com.wxk.vo"/>
<!--指定多个包的三种方式-->
<!--第一种方式:使用多次组件扫描器,指定不同的包-->
<context:component-scan base-package="com.wxk.vo"/>
<context:component-scan base-package="com.wxk.service"/>
<!--第二种方式:使用分隔符(,或者;)分隔多个包名-->
<context:component-scan base-package="com.wxk.service;com.wxk.vo"/>
<!--第三种方式:指定父包-->
<context:component-scan base-package="com.wxk"/>
如果component-scan无法被识别,可以手动引入约束xmlns:context="http://www.springframework.org/schema/context"
同时加入http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
具体如下
2.7、property-placeholder加载资源文件
<!--加载属性配置文件-->
<context:property-placeholder location="classpath:test.properties"/>
加载的文件可以用${}调用
2.8、aspectj-autoproxy自动代理生成器
相关信息点击跳转
3、spring框架的用法
首先,要使用spring框架,需要先在maven中加入相关依赖,如下:
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
3.1、创建spring容器、从容器中获得对象
//使用spring容器创建对象
//1.指定spring配置文件的名称
String config="beans.xml";
//2.创建表示spring容器的对象ApplicationContext
//ApplicationContext表示spring容器,通过容器就能获取对象了
//ClassPathXmlApplicationContext表示从编译后target目录下的类路径(classes)中加载spring的配置文件
//这里有一点要注意的是,spring默认创建对象的时间是当spring容器被创建的时候,这时spring会创建配置文件中所有的对象。创建对象时,spring默认调用的是无参构造方法。
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
在编译后资源文件中的spring配置文件会在类路径下生成
//3.从容器中获取某个对象
//getBean("配置文件中bean的id值")
SomeService someService= (SomeService) ac.getBean("someService");
//4.使用spring创建好的方法
someService.dosome();
3.2、获取容器中对象的数目和名称
String config="beans.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
//使用spring提供的方法获取容器中对象的数量
int num=ac.getBeanDefinitionCount();
System.out.println("容器中对象数目:"+num);
//获取容器中对象的名称
String names[] =ac.getBeanDefinitionNames();
for (String name:names){
System.out.println("对象名称"+name);
}
3.3、DI(配置文件实现)
<!--
声明Student对象
注入:就是赋值的意思
简单类型:spring中规定java的基本数据类型和String都是简单类型
DI(配置文件实现):给属性赋值
1.set注入(设值注入):spring调用类的set方法完成属性赋值,如果类没有相应的set方法,spring会报错
1)简单的set注入语法规则
<bean id="xxx" class="xxx">
<property name="属性名字" value="该属性的值"/>//一个property只能给一个属性赋值,赋值仅仅只是调用的set方法,不管set方法中是否是赋值
<property ...../>
</bean>
2)引用类型的set注入:spring调用类的set方法
<bean id="xxx" class="xxx">
<property name="属性名称" ref="bean的id"/>
</bean>
2.构造注入:spring调用类的有参构造方法,在创建对象的同时,在构造方法中给属性赋值
构造注入使用<constructor-arg>标签
<constructor-arg>标签:一个<constructor-arg>标签只能表示构造方法的一个参数,因此该标签的数量应和想要赋值的属性数量相同
<constructor-arg>标签属性:
name:表示构造方法的形参名
index:表示构造方法的参数的位置,参数从左到右位置是0,1,2……的顺序
value:构造方法的形参是简单类型的,使用value
ref:构造方法的形参是引用类型的,使用ref
-->
<!--set注入-->
<bean id="mySchool" class="com.wxk.vo.School">
<property name="name" value="北京大学"/>
<property name="address" value="北京海淀区"/>
</bean>
<bean id="myStudent" class="com.wxk.vo.Student">
<property name="name" value="张三"/><!--setName-->
<property name="age" value="20"/>
<!--引用类型-->
<property name="school" ref="mySchool"/>
</bean>
<!--
构造注入
在构造注入中,<constructor-arg>标签需要name或者index二者选一个作为唯一标识符,如果都没有也行,都没有仅有value或ref属性则需要<constructor-arg>
标签中value(或ref)的值按构造函数中形参相对应的顺序从上往下排列
-->
<bean id="myStudent2" class="com.wxk.vo.Student">
<constructor-arg name="name" index="0" value="李四"/>
<constructor-arg name="age" index="1" value="21"/>
<constructor-arg name="school" index="2" ref="mySchool"/>
</bean>
3.3.1、引用类型的自动注入
如果一个类中的引用属性很多,那么就会重复很多次<property name="xxx" ref="xxx"/>
,这就会使得代码存在大量的冗余,为了减轻这种冗余,spring提供的方法中有两种常用的自动注入引用属性的方法byName和byType
<!--
引用类型的自动注入:spring框架根据某些规则可以给引用类型赋值,使用的规则通常是byName,byType
1.byName(按名称注入):java类型中引用类型的属性名和spring(容器)配置文件中<bean>的id名称一样,且数据类型一致,这样spring能
直接将<bean>赋值给引用属性
语法:
<bean id="xxx" class="xxx" autowire="byName">
任意
</bean>
2.byType(按类型注入):java类中引用类型的数据类型和spring(容器)配置文件中<bean>的class是“同源关系”,这样<bean>能直接赋值
给引用类型
同源关系:1)java类中引用类型的数据类型和bean中class的值是一样的
2)java类中引用类型的数据类型(父)和bean(子)的class是父子关系
3)java类中引用类型的数据类型(接口)和bean(实现类)的class是接口与实现类关系
语法:
<bean id="xxx" class="xxx" autowire="byType">
任意
</bean>
-->
<!--byName-->
<bean id="school" class="com.wxk.vo.School">
<property name="name" value="北京大学"/>
<property name="address" value="北京海淀区"/>
</bean>
<bean id="myStudent" class="com.wxk.vo.Student" autowire="byName">
<property name="name" value="张三"/><!--setName-->
<property name="age" value="20"/>
<!--引用类型-->
<!--<property name="school" ref="mySchool"/>-->
</bean>
<!--byType-->
<bean id="myStudent2" class="com.wxk.vo.Student" autowire="byType">
<property name="name" value="李四"/><!--setName-->
<property name="age" value="20"/>
<!--引用类型-->
<!--<property name="school" ref="mySchool"/>-->
</bean>
3.4、DI(注解实现)
关于DI的使用,建议是需要经常更改的对象,使用配置文件来实现,反之,不是需要经常更改的用注解实现
使用注解的步骤:
1.加入maven依赖spring-context,在你加入spring-context的同时,spring也会间接加入spring-aop的依赖(打开包的扩展就可以看见),而要使用注解就必须使用spring-aop的依赖。
2.在类中加入spring 的注解(多个不同功能的注解)
3.在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你项目中的位置
主要学习的注解有@Component,@Repository@Service@Controller,@Value,@Autowired,@Resource
3.4.1、@Component
/*
* @Component:创建对象的,等同于<bean>的作用
* 属性:value:对象的名称,也就是bean的id,value值是唯一的,创建的对象在整个spring中就一个
* 位置:在需要创建对象的类的上方
*
* @Component(value = "student1")等同于
* <bean id="student1" class="com.wxk.vo.Student">
* */
@Component(value = "student1")
public class Student {
……
}
注解中的value也能够省略(一般最常用的注解方式),如
@Component("student1")
public class Student {
……
}
还能够不指定对象名称,由spring来提供默认名称:类名的首字母小写,如
@Component
public class Student {
……
}
//在获取该对象时使用ac.getbean("student"),其中student为类名的首字母小写
3.4.2、@Repository@Service@Controller
spring中和@Component 功能一样,创建对象的注解还有:
1.@Repository(持久层):放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的
2.@Service(业务层):放在service的实现类上面,创建service对象,service对象是做业务处理的,可以有业务功能的
3.@Controller(控制器):放在控制器(处理器)类的上面,创建controller对象,controller能够接受用户提交的参数,显示请求的处理结果
以上三个注解的使用语法和@Component一致,都能创建对象,但是这三个注解还有额外的功能,是用来给项目分层的。
3.4.3、@Value
/*
* @Value:简单类型的属性赋值
* 属性:value是String类型的,表示简单类型的属性值
* 位置:1.在属性定义的上面,无需set方法,直接通过反射给属性赋值,推荐使用。
* 2.在set方法的上面。
* */
@Value("张三")
private String name;
@Value("20")
private int age;
3.4.4、@Autowired
/*
* 引用类型的属性赋值
* @Autowired:spring框架提供的注解,实现引用类型的赋值
* spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName、byType(默认)
* 属性:required,是一个boolean类型的,默认是true
* required=true:表示应用类型赋值失败,程序报错,并终止执行。推荐使用
* required=false:表示应用类型赋值失败,程序正常执行,应用类型为null
* 位置:1.在属性定义的上面,无需set方法,推荐使用
* 2.在set方法的上面
* */
@Autowired
private School school;
/*
* 如果要用byName方式,需要做的是:
* 1.在属性上面加@Autowired
* 2.在属性上面加@Qualifier(value="bean的id"):表示使用指定名称的bean完成赋值
* 两者没有先后顺序
* */
@Autowired
@Qualifier("mySchool")
private School school;
3.4.5、@Resource
/*
* 引用类型
* @Resource:来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值
* 使用的也是自动注入的原理,支持byName,byType,默认是byName
* 位置:1.在属性定义的上面,无需set方法,推荐使用
* 2.在set方法的上面
*
* 若想@Resource只使用byName方式,需要增加一个属性name
* name的值是bean的id
* */
@Resource(name = "mySchool")
private School school;
3.5、动态代理
动态代理:可以在程序执行过程中,创建代理对象。 通过代理对象执行方法,给目标类的方法增加额外的功能(功能增强)。jdk动态代理实现步骤:
1.创建目标类,someServiceImpl目标类,给它的doSome,doOther 增加事务。
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("执行了someServiceImpl的doSome()方法");
}
@Override
public void doOther() {
System.out.println("执行了someServiceImpl的doOther()方法");
}
}
2.创建InvocationHandler接口的实现类,在这个类实现给目标代码增加功能。
public class MyInvocationHandler implements InvocationHandler {
//目标对象
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过代理对象执行方法时,会调用这个invoke
//执行结果
Object res=null;
System.out.println("这在加入在目标方法执行前需要执行的方法");
//执行目标类的方法,通过Method类实现
res=method.invoke(target,args);
System.out.println("在这加入在目标方法执行后需要执行的方法");
//目标方法的执行结果
return res;
}
}
3.使用jdk中类proxy,创建代理对象。实现创建对象的能力。
//使用jdk的Proxy创建代理对象
//创建目标对象
SomeService target=new SomeServiceImpl();
//创建InvocationHandler对象
InvocationHandler handler =new MyInvocationHandler(target);
//使用Proxy创建代理
SomeService proxy= (SomeService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
//通过代理执行方法,会调用handler中的invoke()
proxy.doSome();
3.6、Aop实现
Aop是一个规范,是动态代理的一个规范化,一个标准
Aop的技术实现框架:
1.spring:spring在内部实现了aop规范,能做aop的工作。
spring主要在事务处理时使用aop。
一般项目开发很少使用spring的aop实现,因为spring的aop比较笨重。
2.apectj:一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
aspectj框架实现aop有两种方式:
1)使用xml配置文件:配置全局事务。
2)使用注解:aspectj有5个注解@Before、@AfterReturning、@Around、@AfterThrowing、@After,在项目中要做aop功能,一般都用注解。
3.6.1、aspectj框架的使用
在使用aspectj框架之前,需要先加上依赖
<!--aspectj依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
1.aspectj框架的使用流程
1.创建切面类:普通类
1)在类的上面加入@Aspect
2)在类中定义方法,方法就是切面要执行的功能代码
在方法的上面加入aspectj中的通知注解,例如@Before
还需要指定切入点表达式execution()
/*
* @Aspect:是aspectj框架中的注解
* 作用:表示当前类是切面类
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面类的功能代码
* 位置:在类定义的上面
* */
@Aspect
public class MyAspect {
/*
* 定义方法,方法是实现切面类功能的
* 方法定义的要求:
* 1.必须是公共方法public
* 2.方法没有返回值
* 3.方法名称可以自定义
* 4.方法可以有参数,也可以没有参数
* 如果方法有参数,参数不能自定义,有几个参数类型可以使用
* */
@Before("execution(public void com.wxk.service.SomeServiceImpl.do*(..))")
public void myBefore(){
//切面类执行的功能代码
System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
}
}
2.创建spring的配置文件:声明对象,把对象交给容器统一管理
声明对象你可以使用注解或者xml配置文件
1)声明目标对象
2)声明切面类对象
3)声明aspectj框架中的自动代理生成器标签。
自动代理生成器:用来完成代理对象的自动创建功能的。
<!--声明目标对象-->
<bean id="someService" class="com.wxk.service.SomeServiceImpl"/>
<!--声明切面类对象-->
<bean id="myAspect" class="com.wxk.aspect.MyAspect"/>
<!--
声明自动代理生成器:使用aspectj框架内部的功能,创建目标代理的对象。
创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建为代理对象。
所以目标对象就是被修改后的代理对象
aspectj-autoproxy:会扫描spring容器中的所有目标对象,一次性都生成代理对象。
-->
<aop:aspectj-autoproxy/>
<aop:aspectj-autoproxy/>
的约束如下
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
2.切面的执行时间(Advice)
在aspectj框架中使用注解表示,也可以使用xml配置文件中的标签
1)@Before
/*
*@Before:前置通知注解
* 属性: value:是切入点表达式,表示切面的功能执行的位置
* 位置:方法的上面
* 特点:1.在目标方法之前先执行的
* 2.不会改变目标方法的执行结果
* 3.不会影响目标方法的执行
* */
@Before("execution(public void com.wxk.service.SomeServiceImpl.do*(..))")
public void myBefore(){
//切面类执行的功能代码
System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
}
2)@AfterReturning
/*
* 后置通知定义方法,方法是实现切面功能的
* 方法的定义要求:
* 1.公共方法public
* 2.方法没有返回值
* 3.方法名称自定义
* 4.方法有参数,推荐是Object,参数自定义名称
* */
/*
* @AfterReturning:后置通知
* 属性:1.value 切入点表达式
* 2.returning 自定义的变量,表示目标方法的返回值的。
* 自定义变量名必须和通知方法的形参名一样。
* 位置:在方法定义的上面
*
* 特点:1.在目标方法之后执行
* 2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
* 3.可以修改这个返回值
* */
@AfterReturning(value = "execution(* *..service.*.*(..))",returning = "obj")
public void myAfterReturning(Object obj){
//Object obj:是目标方法执行之后的返回值,可以根据返回值做切面功能处理
System.out.println("后置通知接受方法返回值:"+obj);
School school= (School) obj;
school.setName("北京大学");
System.out.println("==========方法执行结束=============");
}
值得注意的是,后置通知所接收的参数如果是引用类型的参数,将其修改后,如上文school.setName,也会影响到接受此返回值的方法。
3)@Around
/*
* 环绕通知方法的定义格式
* 1.公共方法public
* 2.必须有一个返回值,推荐使用Object
* 3.方法名称自定义
* 4.方法有参数,固定的参数ProceedingJoinPoint
* */
/*
* @Around:环绕通知
* 属性:value 切入点表达式
* 位置:在方法的定义上面
* 特点:1.它是功能最强的通知
* 2.在目标方法的前后都能增强功能
* 3.控制目标方法是否被调用执行
* 4.修改原来的目标方法的执行结果。影响最后的调用结果
* 环绕通知等同于jdk的动态代理,InvocationHandler接口
* 参数:ProceedingJoinPoint 等同于 jdk动态代理中的Method
* 作用:执行目标方法
* 返回值:就是目标方法的执行结果,可以被修改。
* */
@Around(value = "execution(* *..service.*.*(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
//实现环绕通知
Object result=null;
System.out.println("========="+pjp.getSignature().getName()+"开始执行============");
//1.目标方法调用
result=pjp.proceed();//相当于method.invoke()
//2.在目标方法的前或者后添加功能或者事务。
System.out.println("========="+pjp.getSignature().getName()+"执行结束============");
return result;
}
4)@AfterThrowing
/*
* 异常通知方法的定义格式
* 1.公共方法public
* 2.没有返回值
* 3.方法名称自定义
* 4.方法的参数有一个Exception,如果还有一个参数,那就是JoinPoint
* */
/*
* @AfterThrowing:异常通知
* 属性:1.value 切入点表达式
* 2.throwing 自定义的变量,表示目标方法抛出的异常对象
* 变量名必须和方法的参数一样
* 特点:1.在目标方法抛出时执行
* 2.可以做异常的监控程序,监控目标方法执行时是不是有异常。
* 如果有异常,可以发送邮件,短信进行通知
* */
@AfterThrowing(value = "execution(* *..service.*.*(..))",throwing = "ex")
public void myAfterThrowing(JoinPoint jp,Exception ex){
System.out.println(jp.getSignature()+"方法出现异常");
}
5)@After
/*
* 最终通知方法的定义格式
* 1.公共方法public
* 2.没有返回值
* 3.方法名称自定义
* 4.方法没有参数,如果有一个参数,那就是JoinPoint
* */
/*
* @After:最终通知
* 属性:value 切入点表达式
* 位置:方法的上面
* 特点:1.总是会执行
* 2.在目标方法之后执行的
* 一般是用来做资源清除工作的。
* 执行顺序是环绕通知--》最终通知--》后置通知
* */
@After(value = "execution(* *..service.*.*(..))")
public void myAfter(){
System.out.println("执行最终通知");
}
6) 通知方法中的参数JoinPoint
/*
* 指定通知方法中的参数:JoinPoint
* JoinPoint:业务方法,要加入切面功能的业务方法
* 作用:可以在通知方法中获取目标方法执行的信息,例如方法名称,方法的实参等
* 如果你的切面功能中需要用到方法的信息,就加入JoinPoint
* 这个JoinPoint参数的值是由框架赋予,必须是第一个位置的参数
* */
@Before("execution(public void com.wxk.service.SomeServiceImpl.do*(..))")
public void myBefore(JoinPoint jp){
//获取方法的完整定义
System.out.println("方法的定义:"+jp.getSignature());
System.out.println("方法的名称:"+jp.getSignature().getName());
//获取方法的实参
Object args[]=jp.getArgs();
for (Object arg:args){
System.out.println("参数:"+arg);
}
//切面类执行的功能代码
System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
}
7) Pointcut注解,切入点表达式的复用
/*
* @Pointcut:定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的。
* 可以使用@Pointcut
* 属性:value 切入点表达式
* 位置:在自定义的方法上面
* 特点:当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。
* 其他的通知中,value属性就可以使用这个方法名称,代替切入点表达式了。
* */
@Pointcut("execution(* *..service.*.*(..))")
public void mypt(){
//不需要代码
}
@After("mypt()")
public void myAfter(){
System.out.println("执行最终通知");
}
3.aspectj的切入点表达式
表达式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
解释:
modifiers-pattern 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选部分(是否使用)
加粗部分为必须使用的,实际项目开发中往往也只用这两个
以上表达式共4个部分
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
表达式中可使用以下符号
符号 | 意义 |
* | 0至多个任意字符 |
.. | 用在方法参数中,表示任意多个参数 用在包名后,表示当前包及其子包路径 |
+ | 用在类名后,表示当前类及其子类 用在接口后,表示当前接口及其实现接口 |
execution(public * *(…))
指定切入点为:任意公共方法
execution(* set*(…))
指定切入点为:任意返回值,任意一个以“set”开始的方法
execution(* com.wxk.service..(…))
指定切入点为:任意返回值的,定义在com.wxk.service包下的所有类的所有方法(不包括service的子包)
execution(* com.wxk.service….(…))
指定切入点为:任意返回值的,定义在com.wxk.service包和其子包下的所有类的所有方法
execution(* …service..*(…))
指定切入点为:任意返回值的,所有包下的service子包的所有类下的所有方法
execution(* .service..*(…))
指定切入点为:任意返回值的,只有一级包下的service子包的所有类下的所有方法
4.jdk动态代理和cglib动态代理
目标类没有接口,那么spring框架会自动调用cglib动态代理,反之,目标类有接口,那么spring框架则会调用jdk动态代理。
如果你期望目标类有接口也使用cglib动态代理,那么可以进行如下设置
<!--
proxy-target-class="true":告诉框架,要使用cglib动态代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"/>