代理与SpringAop
-
springAop概述
AOP 是 OOP 的延续,是 Aspect Oriented Programming 的缩写,意思是面向切面编程。可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP设计模式孜孜不倦追求的是调用者和被调用这之间的解耦,AOP 可以说也是这种目标的一种实现。本质上aop就是代理思想实现的
我们现在做的一些非业务,如:日志、事务、安全等都会写在业务代码中(也即是说,这些非业务类横切与业务类),但这些代码往往是重复,复制-粘贴式的代码会给程序带来不便,AOP 就实现了把这些业务与需求分开来做。这种解决的方式也成代理机制。
SpringAop术语
- 连接点(join Point):程序执行的某个特定位置,比如(类开始初始化前,初始化后,方法前,方法后,异常后) 一个类或者一段程序代码拥有一些具有边界的特定点,这些特定点称为连接点
- 切点(pointcut):每个程序都拥有多个连接点,比如一个类有2个方法,这2个方法就是连接点,连接点就是程序中具体的事物,AOP 通过切点来定位特定的连接点,连接点相当于数据库中的记录,而切点相当于是查询记录的条件。在程序中,切点是连接点位置的集合
- 增强(advice):就是我们要具体做的事情,也就在原有的方法之上添加新的能力
- 目标对象:就是我们要增强的类;
- 引入:特殊的增强,为类添加属性和方法。哪怕这个类没有任何的属性和方法我们也可以通过aop去添加方法和属性实现逻辑
- 织入(weaving):就是把增强添加到目标类具体的连接点上的过程
- 代理:一个类被AOP织入增强后产生的结果类,他是原类和增强后的代理类,更具代理s不同的方式,代理类可能是和原类具有相同接口的类,也可能是原类的子类
- 切面(Aspect):切面由切点和增强组成,他既包含横切的定义,也包括了连接点的定义,spring aop就是负责实施切面的框架,他将切面定义为横切逻辑织入到切面所指定的连接点
SpringAop的实现机制
JDK实现动态代理
代理目标对象接口类:
//目标对象接口
public interface TargetInterface {
public void save();
}
代理目标对象实现类
public class TargetInterfaceImpl implements TargetInterface {
@Override
public void save() {
System.out.println("save running >>>>>");
}
}
通知/增强处理类
public class Advice {
public void before(){
System.out.println("前置增强方法>>>>");
}
public void afterRunning(){
System.out.println("后置增强方法>>>>");
}
}
测试类
//测试类
public class ProxyText {
public static void main(String[] args) {
//目标对象
final TargetInterface target = new TargetInterfaceImpl();
//获得增强对象
final Advice advice = new Advice();
//返回值就是动态生成的代理对象
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(), //目标对象的类加载器
target.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组
new InvocationHandler() {
//调用代理对象,实质执行的是invoke方法
public Object invoke(
Object proxy,
Method method,
Object[] args
) throws Throwable {
advice.before();//前置增强
Object invoke = method.invoke(target, args);//执行目标方法
advice.afterRunning(); //后置增强
return invoke;
}
}
);
//调用代理对象方法
proxy.save();
}
}
运行结果
前置增强方法>>>>
save running >>>>>
后置增强方法>>>>
Process finished with exit code 0
CGlib实现动态代理:
代理目标对象类
public class Target {
public void save() {
System.out.println("save running >>>>>");
}
}
通知/增强处理类
public class Advice {
public void before(){
System.out.println("前置增强方法>>>>");
}
public void afterRunning(){
System.out.println("后置增强方法>>>>");
}
}
测试类
public static void main(String[] args) {
//目标对象
final Target target = new Target();
//增强对象
final Advice advice = new Advice();
//返回值就是动态生成的代理对象 基于cglib
//1、创建增强器
Enhancer enhancer = new Enhancer();
//2、设置父类(目标)
enhancer.setSuperclass(Target.class);
//3、设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(
Object proxy,
Method method,
Object[] args,
MethodProxy methodProxy
) throws Throwable {
//执行前置
advice.before();
Object invoke = method.invoke(target, args);//执行目标
advice.afterRunning();//执行后置
return invoke;
}
});
//4、创建代理对象
Target target1 =(Target) enhancer.create();
target1.save();
}
}
运行结果
前置增强方法>>>>
save running >>>>>
后置增强方法>>>>
Process finished with exit code 0
springAop代理的实现(xml):
创建目标接口和目标实现类(内部有切点)
public interface TargetInterface {
public void save();
}
public class Target implements TargetInterface {
public void save() {
System.out.println("save running >>>>>");
}
}
创建切面类((内部有增强方法)
public class MyAspect {
public void before(){
System.out.println("前置增强 >>>>>>");
}
public void afterReturning(){
System.out.println("后置增强 >>>>>>");
}
}
将目标类和切面类的对象创建权交给 spring
<!--配置目标类-->
<bean id="target" class="com.liu.aop.Target"></bean>
<!--配置切面类-->
<bean id="myAspect" class="com.liu.aop.MyAspect"></bean>
在 applicationContext.xml 中配置织入关系
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
配置切点表达式和前置增强的织入关系
<aop:config>
<!--引用myAspect的Bean为切面对象-->
<aop:aspect ref="myAspect">
<!--配置Target的method方法执行时要进行myAspect的before方法前置增强-->
<aop:before method="before" pointcut="execution(public void
com.liu.aop.Target.save())"></aop:before>
<!--配置Target的method方法执行时要进行myAspect的after方法后置增强-->
<aop:after method="afterReturning" pointcut="execution(public void
com.liu.aop.Target.save())"></aop:before>
</aop:aspect>
</aop:config>
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test1(){
target.save();
}
}
运行结果
前置增强 >>>>>>
save running >>>>>
后置增强 >>>>>>
springAop代理的实现(注解)
创建目标接口和目标类(内部有切点)
public interface TargetInterface {
void method();
}
public class Target implements TargetInterface {
@Override
public void method() {
System.out.println("Target running....");
}
}
创建切面类(内部有增强方法)
public class MyAspect {
//前置增强方法
public void before(){
System.out.println("前置代码增强.....");
}
//后置增强方法
public void afterReturning(){
System.out.println("后置代码增强.....");
}
}
将目标类和切面类的对象创建权交给 spring
Component("target")
public class Target implements TargetInterface {
@Override
public void method() {
System.out.println("Target running....");
}
}
@Component("myAspect")
public class MyAspect {
public void before(){
System.out.println("前置代码增强.....");
}
public void afterReturning(){
System.out.println("后置代码增强.....");
}
}
在切面类中使用注解配置织入关系
@Component("myAspect")
@Aspect
public class MyAspect {
//定义一个切点表达式
@Pointcut("execution(* com.liu.proxy.anno.*.*(..))")
public void pointcut(){}
@Before("MyAspect.pointcut()")
public void before(){
System.out.println("前置代码增强.....");
}
@After("MyAspect.pointcut()")
public void afterReturning(){
System.out.println("后置代码增强.....");
}
}
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test1(){
target.method();
}
}
前置代码增强 >>>>>>
Target running >>>>>
后置代码增强 >>>>>>
切点修饰语法
表达式语法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号* 代表任意
- 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
- 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表
例如:
execution(public void com.liu.aop.Target.method())
execution(void com.liu.aop.Target.*(..))
execution(* com.liu.aop.*.*(..))
execution(* com.liu.aop..*.*(..))
execution(* *..*.*(..))
* _ooOoo_
* o8888888o
* 88" . "88
* (| -_- |)
* O\ = /O
* ____/`---'\____
* .' \\| |// `.
* / \\||| : |||// \
* / _||||| -:- |||||- \
* | | \\\ - /// | |
* | \_| ''\---/'' | |
* \ .-\__ `-` ___/-. /
* ___`. .' /--.--\ `. . __
* ."" '< `.___\_<|>_/___.' >'"".
* | | : `- \`.;`\ _ /`;.`/ - ` : | |
* \ \ `-. \_ __\ /__ _/ .-` / /
* ======`-.____`-.___\_____/___.-`____.-'======
* `=---='
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
欢迎访问我的csdn博客,我们一起成长!!