一、AOP简介

  AOP面向切面编程,是将一种横向抽取机制,将多个类中需要使用的方法提取出来。

  例如,这里有两个类,一个Cat,一个Dog,动物都需要吃饭睡觉,如果按照传统的思想。

  给两类中都添加吃饭和睡觉的方法,如果有成百上千个类呢?是不是很麻烦而且容易出错。

  那么我们将这些方法(切面)提取出来,当Cat和Dog到达合适的时候(切入点)

  就将提取出来的方法插入到执行流程中。

  

  有没有感觉这个有点像代理模式,我们先用静态代理来实现这个想法。

  假设动物的一天是起床->饲养员准备吃的->动物叫(要吃饭了)->饲养员喂食物。

  按照传统的思路是每个动物都写上所有方法,这样很臃肿,我们采用AOP的思想。

  首先所有动物都要吃东西,所以饲养员准备吃的这个方法和喂食这个方法可以提取出来。

  

  Animal接口

public interface Animal {
public void eat(); }

  Cat.java

public class Cat implements Animal{

    @Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("喵喵喵");
}
}

  Dog.java

public class Dog implements Animal{

    @Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("汪汪汪");
}
}

  Proxy.java(代理类)

public class Proxy implements Animal{
private Animal ani; public Proxy(Animal ani) {
this.ani = ani;
} @Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("准备饲料");//切面
ani.eat(); //执行流程
System.out.println("喂食");//切面
}
}

  测试:  

public class Main {
public static void main(String[] args) {
Proxy proxy = new Proxy(new Cat());
proxy.eat();
}
}
运行结果:
准备饲料
喵喵喵
喂食

可以看到,面向切面将重复的部分提取出去了,到了合适的时机(例如猫在喵喵后才喂食)在将方法切入进流程中。

传统的思想就相当于一个动物配一个饲养员,面向切面就相当于所有动物共用一个饲养员。

二、动态代理

静态代理可以实现这种思想,但是一旦需求多了,后期需要添加很多代码,对规模较大的情况不适用。

这时就需要使用动态代理。想像一下每次进入某个地方需要刷卡,然后进入,同时后台还要进行记录。

我们就可以把检查和记录提出出来。

  2.1基于JDK的动态代理

  使用动态代理我们首先来创建接口

  Person.java

public interface Person {
public void intoLib(); //去图书馆
public void intoTeachingBuilding(); //去教学楼
}

  

  Person的实现类Studetn.java

public class Student implements Person{
public void intoLib(){
System.out.println("进入图书馆");
} public void intoTeachingBuilding() {
System.out.println("进入教学楼");
}
}

  我们在来编写切面

  Check.java

public class Check {
public void enter() {
System.out.println("进门检查");
} public void log() {
System.out.println("记录");
}
}

  动态代理类 DP.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class DP implements InvocationHandler{ private Person per;
public Object bind(Person per) {//将代理对象绑定
this.per = per;
//返回代理后的对象 获取类加载器 被代理对象所有接口 自身
return Proxy.newProxyInstance(DP.class.getClassLoader(), per.getClass().getInterfaces(), this);
} //返回的代理后的对象,每次调用方法都由invoke处理
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
// 切面
Check check = new Check();
check.enter();//进门检查
Object obj = method.invoke(per, args);//执行被代理对象的方法
check.log();//记录
return obj;
}
}

  测试:

public class Main {
public static void main(String[] args) {
DP dp = new DP() ;
Person stu = (Person) dp.bind(new Student());
stu.intoLib();
stu.intoTeachingBuilding();
}
}
运行结果:
进门检查
进入图书馆
记录
进门检查
进入教学楼
记录

  2.2基于CGLIB的动态代理

  JDK固然可以实现动态代理,但还有有其局限性,比如被代理需要实现一些接口。

  在对没有实现接口的类进行动态代理可以使用CGLIB。

  CGLIB(Cod Generation Library)是一款高性能开源的代码包,采用字节码技术对代理对象生成一个子类并对其增强来实现。

  CGLIB意见集成到Spring核心包中。(AOP包不要忘记导入)

  

  首先创建被代理类Studetn.java

public class Student{
public void intoLib(){
System.out.println("进入图书馆");
} public void intoTeachingBuilding() {
System.out.println("进入教学楼");
}
}

  

  接着创建切面Check.java

public class Check {
public void enter() {
System.out.println("进门检查");
} public void log() {
System.out.println("记录");
}
}

  接着通过CGLIB创建代理对象

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy; import com.srping.aop.dp.Check; public class CGLIB implements MethodInterceptor{
public Object bind(Object obj) {
//创建增强对象
Enhancer enhancer = new Enhancer();
//设代理对象为父类,用于生成子类并对子类增强
enhancer.setSuperclass(obj.getClass());
//设置回调
enhancer.setCallback(this);
//返回创建的代理类
return enhancer.create();
} //类似动态代理的 invoke
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
//切面
Check check = new Check();
check.enter();//进门检查
Object obj = methodProxy.invokeSuper(proxy, args);//执行被代理对象的方法
check.log();//记录
return obj;
}
}

  最后测试

public class Main {
public static void main(String[] args) {
CGLIB cglib = new CGLIB();
Student stu = (Student)cglib.bind(new Student());
stu.intoLib();
stu.intoTeachingBuilding();
}
}
运行结果:
进门检查
进入图书馆
记录
进门检查
进入教学楼
记录

三、 基于Spring代理类实现AOP

  Spring中有一个ProxyFactoryBena可以实现代理,

  Spring根据切入分类:

  org. aopalliance.intercept.MethodInterceptor (环绕通知)

    org.springframework.aop.MethodBeforeAdvice (前置通知)

    org.springframework.aop.AfterReturningAdvice (后置通知)

    org .springframework.aop.ThrowsAdvice (异常通知)

  ProxyFactoryBean属性

  target:被代理的目标。

  proxyTargetClass:设置是否对类进行代理。

  proxyInterfaces:代理实现的接口,可以是多个(多个时采用<list><vaue>...</list>)

  (如果设置的是对类进行代理,则该属性可以不设置)

  interceptorNames:设置需要织入的advice。

  singleton:返会的代理类是否为单例。

  optimize:设置为true时,强制使用CGLIB。

  我们来看一个例子

  创建接口:

public interface Person {
public void intoLib(); //去图书馆
public void intoTeachingBuilding(); //去教学楼
}

  实现接口的类Studetn.java

public class Student implements Person{
public void intoLib(){
System.out.println("进入图书馆");
} public void intoTeachingBuilding() {
System.out.println("进入教学楼");
}
}

  切面设置环绕通知

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; //环绕通知
public class Check implements MethodInterceptor{
public void enter() {
System.out.println("进门检查");
} public void log() {
System.out.println("记录");
} @Override
public Object invoke(MethodInvocation me) throws Throwable {
// TODO Auto-generated method stub
enter();//进门检查
Object obj = me.proceed();//执行被代理对象自身被调用方法
log();//记录
return obj;
}
}

  beans.xml配置 

<?xml  version="1.0"  encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd" > <!-- 开启注解 -->
<!-- <context:annotation-config/> -->
<!-- 配置Bean的实例 --> <!-- 1.目标类,即被代理的类 -->
<bean id = "student" class = "com.spring.aop.proxyFactoryBean.Student">
</bean>
<!-- 2.通知(切面) -->
<bean id = "check" class = "com.spring.aop.proxyFactoryBean.Check">
</bean>
<!-- 3.使用ProxyFactoryBena定义一个代理类 -->
<bean id = "proxy" class = "org.springframework.aop.framework.ProxyFactoryBean"> <!-- 3.1指定代理方法 false为不进行类代理,代理的是接口。true反之-->
<property name="proxyTargetClass" value = "false"></property>
<!-- 3.2 指定代理实现的接口 如果是proxyTargetClass = true 则该属性可不设置-->
<property name="proxyInterfaces" value="com.spring.aop.proxyFactoryBean.Person"></property>
<!-- 3.3指定目标对象,即被代理类 -->
<property name = "target" ref = "student"></property>
<!-- 3.4指定通知adivce -->
<property name="interceptorNames" value = "check"></property> </bean>
</beans>

  

  测试:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
Person stu = (Person)ac.getBean("proxy"); //由于本例采用代理接口,所以只能强转为Person接口。
stu.intoLib(); //如果想强转为Student,需要采用类代理,proxyTargetClass设置为true
stu.intoTeachingBuilding();
}
}
运行结果:
二月 11, 2019 8:43:56 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@25f38edc: startup date [Mon Feb 11 20:43:56 CST 2019]; root of context hierarchy
二月 11, 2019 8:43:56 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [beans.xml]
进门检查
进入图书馆
记录
进门检查
进入教学楼
记录

  如果采用类代理,则可以将接口全部去掉。

  然后可以将xml文件中的“proxyTargetClass”改为trey、将“proxyInterfaces”去掉。

<!-- 1.目标类,即被代理的类 -->
<bean id = "student" class = "com.spring.aop.proxyFactoryBean.Student">
</bean>
<!-- 2.通知(切面) -->
<bean id = "check" class = "com.spring.aop.proxyFactoryBean.Check">
</bean>
<!-- 3.使用ProxyFactoryBena定义一个代理类 -->
<bean id = "proxy" class = "org.springframework.aop.framework.ProxyFactoryBean"> <!-- 3.1指定代理方法 ture为进行类代理,不进行接口代理。false反之-->
<property name="proxyTargetClass" value = "true"></property> <!-- 3.3指定目标对象,即被代理类 -->
<property name = "target" ref = "student"></property>
<!-- 3.4指定通知adivce -->
<property name="interceptorNames" value = "check"></property> </bean>

  

  测试: 

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
//Person stu_ = (Person)ac.getBean("proxy"); //采用类代理是可以强转为接口,也可以强转为类。
Student stu = (Student)ac.getBean("proxy");
stu.intoLib();
stu.intoTeachingBuilding();
}
}
运行结果:
与上例相同。

  

四、AspectJ

  AspectJ是一个基于Java语言的AOP框架。

  使用AspectJ之前需要先导入两个包:

  -Spring-aspects-x.x.x.RELEASE.jar

  -aspectjweaver-1.8.10.jar

   下载地址:https://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.8.10

  4.1AspectJ基于xml实现AOP

  先来看下xml中关于AOP的一些元素。

  <aop:config>:用于定义切面、切入点、通知等。

    <aop:aspect>:<aop:config>的子元素,用于定义一个切面。

    id:该切面的唯一标识。

    ref:该切面所对应的切面类。

    <aop:pointcut>:<aop:config><aop:aspect>的子元素,用于定义一个切入点。

    如果切入点配置在切面中,则只对当前切面有效,如果定义在<aop:config>中则对所有切面有效。

    id:该切入点的唯一标识。

    execution:切入点表达式。

    <aop:before>:配置前置通知。

    <aop:after-returning>:配置后置通知。

    <aop:around>:配置环绕通知。

    <aop:after-throwing>:配置异常通知。

    <aop:after>:配置最终通知。

    poincut:设置一个切入点标识。

    pointcut-ref:指定一个切入点表达式。

    method:指定当前通知执行的方法。

    throwing:指定一个形参名,用于获取抛出的异常,仅对after-thorwing有效。

    returning:指定一个形参名,用于获取目标方法的返回值,仅对after-returning有效。

下面来看一个例子

/**
* 创建时间:2019年2月16日 下午8:17:43
* 项目名称:Spring
* @author hcf
* @version 1.0
* @since JDK 1.8.0_201
* 文件名称:UserDao.java
* 类说明: UserDao接口
*/ public interface UserDao {
public void add();
public void delete();
public int test();
}
import org.springframework.stereotype.Repository;

/**
* 创建时间:2019年2月16日 下午8:19:01
* 项目名称:SpringAOP
* @author hcf
* @version 1.0
* @since JDK 1.8.0_201
* 文件名称:UserDaoImple.java
* 说 明:UserDao接口的实现类
*/
@Repository("userDao")
public class UserDaoImple implements UserDao{ @Override
public void add() {
// TODO Auto-generated method stub
System.out.println("添加用户");
} @Override
public void delete() {
// TODO Auto-generated method stub
System.out.println("删除用户");
} public int test() {
//int i = 1/0;
System.out.println("test");
return 10;
}
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//切面类
/**
* 创建时间:2019年2月16日 下午8:23:50
* 项目名称:Spring
* @author hcf
* @version 1.0
* @since JDK 1.8.0_201
* 文件名称:MyAspect.java
* 说 明:切面类,定义了各种通知。
*/
public class MyAspect {
//前置通知
public void myBefore(JoinPoint jp) {
System.out.println("前置通知------");
System.out.println("-目标类是:" + jp.getTarget());
System.out.println("-目标方法是:" + jp.getSignature().getName());
} //后置通知 returnVal为xml中指定的形参名
public void myAfter(JoinPoint jp,Object returnVal) {
System.out.println("后置通知------");
System.out.println("-目标方法是:" + jp.getSignature().getName());
System.out.print("-目标方法返回值" + returnVal.toString());
} //环绕通知
public Object myAround(ProceedingJoinPoint pj) throws Throwable{
System.out.println("环绕开始------");
//执行对象本身调用的方法
Object object = pj.proceed();
System.out.println("环绕结束------");
return object;
} //异常通知 e为xml中指定的形参名
public void myThrowing(JoinPoint jp, Throwable e) {
System.out.print("异常通知:" + e.getMessage());
} //最终通知
public void myFinally() {
System.out.println("最终通知");
}
}

beans.xml

<?xml  version="1.0"  encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" >
<!-- 1.目标类 -->
<bean id = "userDao" class = "com.spring.AspectJ.xml.UserDaoImple"> </bean>
<!-- 2.切面类 -->
<bean id = "myAspect" class = "com.spring.AspectJ.xml.MyAspect"></bean>
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref = "myAspect">
<!-- 配置切入点 并为其指定一个名词 -->
<aop:pointcut expression = "execution(* com.spring.AspectJ.xml.*.*(..))" id = "myPointCut"/> <!-- 设置通知和通知的切入点 -->
<!-- 前置通知 指定前置通知执行的方法,并设置切入点 -->
<aop:before method = "myBefore" pointcut-ref = "myPointCut"/>
<!-- 后置通知 指定后置通知执行的方法,并设置切入点 returning指定后置通知方法的形参名,用于获取目标方法的返回值 -->
<aop:after-returning method = "myAfter" pointcut-ref = "myPointCut" returning = "returnVal"/>
<!-- 环绕通知 指定环绕通知执行的方法,并设置切入点-->
<aop:around method="myAround" pointcut-ref = "myPointCut"/>
<!-- 异常通知,如果程序没有发生异常则不会被执行 throwing指定异常通知方法的形参名 -->
<aop:after-throwing method = "myThrowing" pointcut-ref = "myPointCut" throwing = "e" />
<!-- 最终通知 ,无论如何都会执行-->
<aop:after method = "myFinally" pointcut-ref = "myPointCut"/>
</aop:aspect>
</aop:config>
</beans>

测试:

  

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* 创建时间:2019年2月16日 下午8:50:39
* 项目名称:Spring
* @author hcf
* @version 1.0
* @since JDK 1.8.0_201
* 文件名称:Main.java
* 说 明: 测试AspectJ基于XML实现AOP
*/
public class Main {
public static void main(String[] args) {
String beansXmlPathString = "com/spring/AspectJ/xml/beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(beansXmlPathString);
UserDao userao = (UserDao)ac.getBean("userDao");
userao.test();
}
}
运行结果:
二月 18, 2019 10:36:14 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@300ffa5d: startup date [Mon Feb 18 10:36:14 CST 2019]; root of context hierarchy
二月 18, 2019 10:36:14 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/spring/AspectJ/xml/beans.xml]
前置通知------
-目标类是:com.spring.AspectJ.xml.UserDaoImple@341b80b2
-目标方法是:test
环绕开始------
test
最终通知
环绕结束------
后置通知------
-目标方法是:test
-目标方法返回值10

我们将 UserDaoImple中test方法中的//int i = 1/0;的注释去掉,然后再运行看下结果:

二月 18, 2019 10:41:25 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@300ffa5d: startup date [Mon Feb 18 10:41:25 CST 2019]; root of context hierarchy
二月 18, 2019 10:41:25 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/spring/AspectJ/xml/beans.xml]
前置通知------
-目标类是:com.spring.AspectJ.xml.UserDaoImple@341b80b2
-目标方法是:test
环绕开始------
最终通知
异常通知:/ by zeroException in thread "main" java.lang.ArithmeticException: / by zero
at com.spring.AspectJ.xml.UserDaoImple.test(UserDaoImple.java:30)

由上面两个运行结果可以看出,环绕的是目标方法,前置通知最先执行,后置通知最后执行。

最终通知无论如何都会执行。

  4.2AspectJ基于annotation实现AOP

    基于注解实现AOP会比较方便,首先我们来了解几个注解。  

    @Aspect:定义一个切面类。

    @Pointcut:定义一个切入点,value指定切入点表达式,还需定义一个空方法为该切入点的名称。

    @Before:定义前置通知,value属性用于指定切入点。

    @AfterRunning:定义后置通知,value属性指定切入点,returninig属性指定形参名,用于访问方法返回值。

    @Around:定义环绕通知。value属性指定切入点。

    @AfterThrowing:定义异常通知,valuet同上,Throwing指定一个形参名,用于访问抛出的异常。

    @After:定义最终通知,value同上。

下面还是看一个例子理解注解实现AOP

/**
* 创建时间:2019年2月16日 下午8:17:43
* 项目名称:Spring
* @author hcf
* @version 1.0
* @since JDK 1.8.0_201
* 文件名称:UserDao.java
* 类说明: UserDao接口,注解实现
*/ public interface UserDao {
public void add();
public void delete();
public int test();
}
import org.springframework.stereotype.Repository;

/**
* 创建时间:2019年2月16日 下午8:19:01
* 项目名称:SpringAOP
* @author hcf
* @version 1.0
* @since JDK 1.8.0_201
* 文件名称:UserDaoImple.java
* 说 明:UserDao接口的实现类,注解实现AOP
*/
@Repository("userDao")//基于注解实例化Bena
public class UserDaoImple implements UserDao{ @Override
public void add() {
// TODO Auto-generated method stub
System.out.println("添加用户");
} @Override
public void delete() {
// TODO Auto-generated method stub
System.out.println("删除用户");
} public int test() {
//int i = 1/0;
System.out.println("test");
return 10;
}
}
import org.aspectj.lang.JoinPoint;
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;
//切面类 /**
* 创建时间:2019年2月17日 下午8:24:15
* 项目名称:Spring
* @author hcf
* @version 1.0
* @since JDK 1.8.0_201
* 文件名称:MyAspect.java
* 说 明: AspectJ注解实现AOP。
*/ //基于注解定义切面,同时定义当前类
@Aspect
@Component
public class MyAspect {
//定义一个切入点表达式,并设置一个空方法为其命名。
@Pointcut("execution(* com.spring.AspectJ.annotation.*.*(..))")
private void myPointCut() {};//定义一个void空方法为切入点命名,类似 id = "myPointCut" //前置通知,指定切入点
@Before("myPointCut()")
public void myBefore(JoinPoint jp) {
System.out.println("前置通知------");
System.out.println("-目标类是:" + jp.getTarget());
System.out.println("-目标方法是:" + jp.getSignature().getName());
} //后置通知 returnVal为指定的形参名
@AfterReturning(value = "myPointCut()" , returning = "returnVal")
public void myAfter(JoinPoint jp,Object returnVal) {
System.out.println("后置通知------");
System.out.println("-目标方法是:" + jp.getSignature().getName());
System.out.print("-目标方法返回值" + returnVal.toString());
} //环绕通知
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint pj) throws Throwable{
System.out.println("环绕开始------");
//执行对象本身方法
Object object = pj.proceed();
System.out.println("环绕结束------");
return object;
} //异常通知 e为指定的形参名
@AfterThrowing(value = "myPointCut()", throwing = "e")
public void myThrowing(JoinPoint jp, Throwable e) {
System.out.print("异常通知:" + e.getMessage());
} //最终通知
@After("myPointCut()")
public void myFinally() {
System.out.println("最终通知");
}
}

beans.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 扫描指定包下@Component @Controller@Service等这些注解,将其对应类注册 -->
<context:component-scan base-package="com.spring.AspectJ.annotation"></context:component-scan>
<!-- 开启注解 -->
<aop:aspectj-autoproxy/>
</beans>

如果UserDaoImple和MyAspect不使用注解注册,则 context:component-scan可以去掉。

但需要手动为其配置bean:

<bean id = "userDao" class = "com.spring.AspectJ.annotation.UserDaoImple"/>
<bean id = "myAspect" class = "com.spring.AspectJ.annotation.MyAspect"/>

切面类首先是一个类,其次它是一个切面类,只要是类通过Spring实例化就要为其注册。

测试:

  

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext; /**
* 创建时间:2019年2月16日 下午8:50:39
* 项目名称:Spring
* @author hcf
* @version 1.0
* @since JDK 1.8.0_201
* 文件名称:Main.java
* 说 明: 测试AspectJ基于Annotation实现AOP
*/
public class Main {
public static void main(String[] args) {
String beansXmlPathString = "com/spring/AspectJ/annotation/beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(beansXmlPathString);
com.spring.AspectJ.annotation.UserDao userDao = (com.spring.AspectJ.annotation.UserDao)ac.getBean("userDao");
userDao.test();
}
}
运行结果:
二月 18, 2019 11:16:48 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@300ffa5d: startup date [Mon Feb 18 11:16:48 CST 2019]; root of context hierarchy
二月 18, 2019 11:16:48 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/spring/AspectJ/annotation/beans.xml]
环绕开始------
前置通知------
-目标类是:com.spring.AspectJ.annotation.UserDaoImple@17c1bced
-目标方法是:test
test
环绕结束------
最终通知
后置通知------
-目标方法是:test
-目标方法返回值10

1.3(Spring学习笔记)Spring-AOP的更多相关文章

  1. Spring学习笔记之aop动态代理(3)

    Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. Person ...

  2. Spring学习笔记--spring+mybatis集成

    前言: 技术的发展, 真的是日新月异. 作为javaer, 都不约而同地抛弃裸写jdbc代码, 而用各种持久化框架. 从hibernate, Spring的JDBCTemplate, 到ibatis, ...

  3. Spring学习笔记4——AOP

    AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 ...

  4. Spring学习笔记--Spring IOC

    沿着我们上一篇的学习笔记,我们继续通过代码学习IOC这一设计思想. 6.Hello类 第一步:首先创建一个类Hello package cn.sxt.bean; public class Hello ...

  5. Spring学习笔记—Spring之旅

    1.Spring简介     Spring是一个开源框架,最早由Rod Johnson创建,并在<Expert One-on-One:J2EE Design and Development> ...

  6. Spring学习笔记--Spring简介

    1.spring:给软件行业带来了春天; 2.spring的理念:spring框架的初衷是使的现有的更加实用,spring不是创造轮子(技术或框架),而是使现有的轮子更好的运转;spring本身是一个 ...

  7. [Spring学习笔记 4 ] AOP 概念原理以及java动态代理

    一.Spring IoC容器补充(1) Spring IoC容器,DI(依赖注入): 注入的方式:设值方法注入setter(属性注入)/构造子注入(构造函数传入依赖的对象)/字段注入Field(注解) ...

  8. Spring学习笔记2—AOP

    1.AOP概念 AOP(Aspect Oriented Programming):面向切面编程,AOP能够将那些与业务无关,却为业务模块所共同调用的应用(例如事务处理.日志管理.权限控制等)封装起来, ...

  9. Spring学习笔记之AOP配置篇(一)

    [TOC] 1. 创建并声明一个切面 首先,创建一个类,添加@Component注解使其添加到IoC容器 然后,添加@Aspect注解,使其成为一个切面 最后,在配置文件里面,使用<aop:as ...

  10. Spring学习笔记--Spring配置文件和依赖注入

    Spring配置文件 1.alias:设置别名,为bean设置别名,并且可以设置多个别名; <!-- 设置别名 --> <alias name="user" al ...

随机推荐

  1. Active Directory Domain Services in Windows Server 2016/2012

    Applies To: Windows Server 2016, Windows Server 2012 R2, Windows Server 2012 You will find links to ...

  2. PHP 抽象类,接口,抽象方法,静态方法

    1.Abstract class(抽象类) 抽象类是指在 class 前加了 abstract 关键字且存在抽象方法(在类方法 function 关键字前加了 abstract 关键字)的类. 抽象类 ...

  3. {CodeForces】788E New task && 汕头市队赛SRM06 D 五色战队

    D 五色战队 SRM 06 背景&&描述         游行寺家里人们的发色多种多样,有基佬紫.原谅绿.少女粉.高级黑.相簿白等.         日向彼方:吾令人观其气,气成五彩, ...

  4. Linux下Tomcat开机自动启动

    linux下tomcat开机自动启动有两种方法,一种是简单,一种是复杂而又专业的,使用shell脚本要实现,我们一般推荐shell脚本启动方式.下面我们分别介绍这两种方法. 1.shell脚本启动 众 ...

  5. python 匿名函数和递归函数

    匿名函数lambda 匿名函数:lambda  x,y:x+y 上述解释:x,y分别是函数的参数,x+y是函数的返回值 匿名函数的命名规则,用lamdba 关键字标识,冒号(:)左侧表示函数接收的参数 ...

  6. PHP HERE DOCUMENT

    转自: http://www.codeweblog.com/php%E4%B8%ADheredoc%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95/ Here ...

  7. 《linux下进程的创建,执行,监控和终止》

    <linux下进程的创建,执行,监控和终止> http://blog.csdn.net/miss_acha/article/details/43671047 http://blog.csd ...

  8. 【反演复习计划】【51nod1594】Gcd and Phi

    现在感觉反演好多都是套路QAQ…… #include<bits/stdc++.h> using namespace std; ; typedef long long ll; int n,c ...

  9. 【洛谷P3709】大爷的字符串题

    看这题网上居然还没人写blog,怕是都去看洛谷自带的了-- 你才是字符串!你全家都是字符串!这题跟字符串没多大关系,只是出题人lxl想要吐槽某中学而已--... 其实这题说白了就是问区间里出现最多的数 ...

  10. tcpip概述

    网络协议通常分为不同层次进行开发,每一层分别负责不同的通信功能.一个类似TCPIP的协议簇是一组不同层次上的多个协议的组合.TCPIP通常被认为是一个四层协议系统,分为:应用层(telnet/FTP/ ...