那啥我项目构建都是用maven建的,所以直接放pom了哈
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
先来了解一下aop
AOP编程
Aop, aspect object programming 面向切面编程
面向切面编程,就是指在运行时,动态的将代码切入到类的指定方法,指定位置上的编程思想就是面向切面的编程
功能: 让重复代码与业务代码分离!
连接点(joinpoint )
目标对象中,所有可以增强的方法
切入点(pointcut)
执行目标对象方法,动态植入切面代码。(目标对象中,确定要增强的方法)
Advice(通知、增强)
增强的代码
Target(目标对象)
被代理的对象
weaving(织入)
将通知应用的切入点的过程
proxy(代理)
将通知植入到目标对象后,形成代理对象
aspect(切面)
切入点+通知 相结合(集合)
通知说明了干什么和什么时候干(什么时候通过方法名中的befor,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
注意:AOP并不是Spring创立的,Spring只是提供了对aop的支持
之所以叫Spring AOP是因为:
以前我们要使用动态代理,我们需要自己调用下面这个方法:
Proxy.newProxyInstance ( xx , xx ,xx )
生成 代理对象
现在只需通过配置文件或注解就可以实现,所以说Spring能够为容器中管理的对象生成动态代理对象
Spring AOP 中采用了两种动态代理技术
1.jdk代理技术,是利用JDKAPI, 动态的在内存中构建代理对象
注意:被代理的对象必须要实现接口,才能产生代理对象,如果没有接口就无法使用此技术
2.cglib代理(第三方),可以对任何类生成代理,代理的原理是对目标对象进行继承代理
注意:
1) 需要引入cglib – jar文件, 但是spring的核心包中已经包括了cglib功能,所以直接引入spring-core.jar即可。
2)引入功能包后,就可以在内存中动态构建子类(代理对象继承了被代理对象)
3)代理的类不能为final, 否则报错。
4) 目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。
下面是手动使用这两种技术的案例:
jdk代理
接口类
@Component("iUserService")
public interface IUserService {
void save ();
void delete ();
void update ();
void find ();
}
实现类
@Component("userServiceImpl")
public class UserServiceImpl implements IUserService {
public void save() {
System.out.println("save");
}
public void delete() {
System.out.println("delete");
}
public void update() {
System.out.println("update");
}
public void find() {
System.out.println("find");
}
}
代理工厂
@Component("userServiceProxyFactory")
public class UserServiceProxyFactory {
//生成代理类
public IUserService getUserServiceProxy ( UserServiceImpl userServiceImpl )
{
//Proxy.newProxyInstance接受三个参数
/*loader:定义了代理类的ClassLoder;
interfaces:代理类实现的接口列表
h:调用处理器*/
IUserService iUserSerivice =
(IUserService) Proxy.newProxyInstance(
userServiceImpl.getClass().getClassLoader(),
userServiceImpl.getClass().getInterfaces(),
new InvocationHandler() {
//proxy 当前代理类 method 当前代理类要执行的方法 args方法的参数
public Object invoke( Object proxy , Method method , Object[] args) throws Throwable {
System.out.println("start");
Object obj = method.invoke( userServiceImpl , args ) ;//执行目标方法
System.out.println("end");
return obj ;
}
}
) ;
return iUserSerivice ;
}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)//使用帮我们自动创建容器,就不用自己手动创建Spring容器
@ContextConfiguration("classpath:bean.xml")//指定配置文件路径
public class Demo {
@Resource(name="userServiceProxyFactory")
private UserServiceProxyFactory userServiceProxyFactory ;
@Resource(name="userServiceImpl")
private UserServiceImpl userServiceImpl ;
@Test
public void testProxy ()
{
IUserService iUserService = userServiceProxyFactory.getUserServiceProxy(userServiceImpl) ;
iUserService.save();
}
}
结果:
start
save
end
cglib代理
/**
*
*/
package cn.itcast.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author Administrator
*
*/
@Component("car")
public class Car {
@Value("兰博基尼")
private String name ;
@Value("原谅绿")
private String color ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void p ()
{
System.out.println( name + " " + color );
}
}
/**
*
*/
package cn.itcast.proxy;
import java.lang.reflect.Method;
import javax.annotation.Resource;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Component;
import cn.itcast.bean.Car;
/**
* @author Administrator
*
*/
@Component("carProxyFactory")
public class CarProxyFactory implements MethodInterceptor {
//使用cglib代理为car对象生成代理对象
@Resource(name="car")
private Car car ;
//创建代理对象
public Car getCarProxy ()
{
//帮我们生成代理
Enhancer en = new Enhancer() ;
//设置对谁进行代理
en.setSuperclass(car.getClass());
//设置代理要做什么
en.setCallback( this );
//创建代理对象
return (Car) en.create() ;
}
/*
* @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy)
*/
@Override //形参从左到右 依次为:被代理原始对象 , 被代理原始对象方法 ,运行期参数 ,代理方法对象
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("Start");
//执行被代理对象的方法
Object returnValue = method.invoke(car, args) ;
System.out.println("end");
return returnValue;
}
}
package cn.itcast.annotation;
import javax.annotation.Resource;
import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.itcast.bean.Car;
import cn.itcast.bean.IUserService;
import cn.itcast.bean.UserServiceImpl;
import cn.itcast.proxy.CarProxyFactory;
import cn.itcast.proxy.UserServiceProxyFactory;
/**
* @author Administrator
* Spring整合Junit测试(需导入Spring-test包)
* 光看一个测试方法感觉并没有节省代码,但是测试方法多时有奇效!
*/
@RunWith(SpringJUnit4ClassRunner.class)//使用帮我们自动创建容器,就不用自己手动创建Spring容器
@ContextConfiguration("classpath:bean.xml")//指定配置文件路径
public class Demo {
@Resource(name="carProxyFactory")
private CarProxyFactory carProxyFactory ;
@Test
public void testCGProxy ()
{
Car car = carProxyFactory.getCarProxy() ;
car.p();
}
}
结果:
Start
兰博基尼 原谅绿
end
在Spring 中使用aop
xml配置方式
目标对象
@Component("userServiceImpl")
public class UserServiceImpl implements IUserService {
public void save() {
System.out.println("save");
}
public void delete() {
System.out.println("delete");
}
public void update() {
System.out.println("update");
}
public void find() {
System.out.println("find");
}
}
通知
public class MyAdvice {
//前置通知(目标方法运行之前调用)
public void before ()
{
System.out.println("这是前置通知");
}
//后置通知(如果出现异常不会调用)
public void afterRuturn ()
{
System.out.println("后置通知(如果出现异常不会调用)");
}
//环绕通知(在目标方法之前和之后都调用)写法比较特殊
public Object around ( ProceedingJoinPoint pjp ) throws Throwable
{
System.out.println("环绕通知-前部分");
Object proceed = pjp.proceed() ;//调用目标方法
System.out.println("环绕通知-后部分");
return proceed ;
}
//异常拦截通知(出现异常就会调用)
public void afterException ()
{
System.out.println("出现异常啦");
}
//后置通知(无论出现异常都会调用,在方法运行后)
public void after ()
{
System.out.println("后置通知");
}
}
配置(配置中的切入点表达式,文章最后有介绍)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置目标对象 -->
<bean name="userServiceImpl" class="cn.itcast.bean.UserServiceImpl">
</bean>
<!-- 配置通知对象 -->
<bean name="myAdvice" class="cn.itcast.bean.MyAdvice">
</bean>
<!-- 配置将通知织入对象 -->
<aop:config>
<!-- 配置切入点
expression后面加上切入点表达式:匹配cn.itcast.bean包下所有类的所有方法,方法形参不限
-->
<aop:pointcut expression="execution(* cn.itcast.bean.*.*(..))" id="pt"/>
<!-- 配置通知 ref指定通知对象-->
<aop:aspect ref="myAdvice">
<!-- 指定名为before的方法作为前置通知 pointcut-ref 使用在哪个切入点上-->
<aop:before method="before" pointcut-ref="pt"/>
<aop:after-returning method="afterRuturn" pointcut-ref="pt"/>
<aop:around method="around" pointcut-ref="pt"/>
<aop:after-throwing method="afterException" pointcut-ref="pt"/>
<aop:after method="after" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
测试
package cn.itcast.annotation;
import javax.annotation.Resource;
import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.itcast.bean.Car;
import cn.itcast.bean.IUserService;
import cn.itcast.bean.UserServiceImpl;
import cn.itcast.proxy.CarProxyFactory;
import cn.itcast.proxy.UserServiceProxyFactory;
/**
* @author Administrator
*/
@RunWith(SpringJUnit4ClassRunner.class)//使用帮我们自动创建容器,就不用自己手动创建Spring容器
@ContextConfiguration("classpath:cn/itcast/bean/bean.xml")//指定配置文件路径
public class Demo2 {
@Resource(name="userServiceImpl")
private IUserService userServiceImpl ;
@Test
public void testAOP ()
{
userServiceImpl.save();
}
}
注解方式
目标类同上一个案例所以不贴了
通知
/**
*
*/
package cn.itcast.annotationaop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* @author Administrator
*
*/
@Aspect //表示该类是一个通知类
@Component("myAdvice")
public class MyAdvice {
//前置通知(目标方法运行之前调用)
//指定该方法是前置通知,并通过切入点表达式指定切入点
@Before("execution(* cn.itcast.bean.*.*(..))") //
public void before ()
{
System.out.println("这是前置通知");
}
//后置通知(如果出现异常不会调用)
@AfterReturning("execution(* cn.itcast.bean.*.*(..))")
public void afterRuturn ()
{
System.out.println("后置通知(如果出现异常不会调用)");
}
//环绕通知(在目标方法之前和之后都调用)
@Around("execution(* cn.itcast.bean.*.*(..))")
public Object around ( ProceedingJoinPoint pjp ) throws Throwable
{
System.out.println("环绕通知-前部分");
Object proceed = pjp.proceed() ;//调用目标方法
System.out.println("环绕通知-后部分");
return proceed ;
}
//异常拦截通知(出现异常就会调用)
@AfterThrowing("execution(* cn.itcast.bean.*.*(..))")
public void afterException ()
{
System.out.println("出现异常啦");
}
//后置通知(无论出现异常都会调用,在方法运行后)
@After("execution(* cn.itcast.bean.*.*(..))")
public void after ()
{
System.out.println("后置通知");
}
}
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启使用注解完成织入 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
优化一下通知类
**
*
*/
package cn.itcast.annotationaop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author Administrator
*
*/
@Aspect //表示该类是一个通知类
@Component("myAdvice")
public class MyAdvice {
@Pointcut("execution(* cn.itcast.bean.*.*(..))")
public void pc ()
{
}
//前置通知(目标方法运行之前调用)
//指定该方法是前置通知,并通过切入点表达式指定切入点
@Before("MyAdvice.pc()") //
public void before ()
{
System.out.println("这是前置通知");
}
//后置通知(如果出现异常不会调用)
@AfterReturning("MyAdvice.pc()")
public void afterRuturn ()
{
System.out.println("后置通知(如果出现异常不会调用)");
}
//环绕通知(在目标方法之前和之后都调用)
@Around("MyAdvice.pc()")
public Object around ( ProceedingJoinPoint pjp ) throws Throwable
{
System.out.println("环绕通知-前部分");
Object proceed = pjp.proceed() ;//调用目标方法
System.out.println("环绕通知-后部分");
return proceed ;
}
//异常拦截通知(出现异常就会调用)
@AfterThrowing("MyAdvice.pc()")
public void afterException ()
{
System.out.println("出现异常啦");
}
//后置通知(无论出现异常都会调用,在方法运行后)
@After("MyAdvice.pc()")
public void after ()
{
System.out.println("后置通知");
}
}
切入点表达式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
配置目标对象
<!-- -->
<bean id="userDao" class="cn.itcast.g_pointcut.UserDao"></bean>
<bean id="orderDao" class="cn.itcast.g_pointcut.OrderDao"></bean>
配置通知对象
<!-- -->
<bean id="aop" class="cn.itcast.g_pointcut.Aop"></bean>
配置将通知织入对象
<!-- -->
<aop:config>
<!-- 配置切入点 匹配哪些方法 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.*.*(..))" id="pt"/>-->
<!-- 【匹配所有public方法】 -->
<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
<!-- 【匹配所有save开头的方法 】 -->
<!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>-->
<!-- 【匹配指定类的指定public方法, 匹配时候一定要定位到方法】 -->
<!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>-->
<!-- 【匹配指定类的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>-->
<!-- 【匹配指定包,以及其自包下所有类的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
<!-- 【多个表达式】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 下面2个且关系的,没有意义 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) && execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 【取非值】 -->
<!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<aop:pointcut expression=" not execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>
<!-- 切面 -->
<aop:aspect ref="aop">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>