一、简单的spring实现(annotation方式)
bean类
package hello;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HelloWorld {
private String Msg;
public void Hello() {
System.out.println("Hello:" + Msg);
}
@Bean(name = "helloworldbean")
public HelloWorld helloWorldBean() {
HelloWorld hw = new HelloWorld();
hw.Msg = "How are you ?";
return hw;
}
}
测试类:
package hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestHelloWorld {
/**
* @param args
*/
private static ApplicationContext ctx;
public static void main(String[] args) {
// TODO 自动生成的方法存根
ctx = new AnnotationConfigApplicationContext(HelloWorld.class);
HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloworldbean");
helloWorld.Hello();
}
}
运行结果:
2017-10-24 00:34:15 [org.springframework.context.annotation.AnnotationConfigApplicationContext]-[INFO]
Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@12bb4df8: startup date [Tue Oct 24 00:34:15 CST 2017]; root of context hierarchy
Hello:How are you ?
(xml方式)
bean类
package com.hello;
public class XmlHello {
private String name;
public void setName(String name) {
this.name = name;
}
public void printHello() {
System.out.println("Spring : Hello ! " + name);
}
}
xml配置文件applicationContext.xml(放在包com.hello下)
<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-4.3.xsd">
<bean id="helloBean" class="com.hello.XmlHello">
<property name="name" value="Yiibai" />
</bean>
</beans>
测试类:
package com.hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppXmlHello {
/**
* @param args
*/
private static ApplicationContext ctx;
public static void main(String[] args) {
// TODO 自动生成的方法存根
ctx = new ClassPathXmlApplicationContext(
"com/hello/applicationContext.xml");
XmlHello helloWorld = (XmlHello) ctx.getBean("helloBean");
helloWorld.printHello();;
}
}
二、注解方式实现aop(需要导入aspectjrt-1.8.11.jar,aspectjweaver-1.8.11.jar ):
package hello;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class HelloAop {
// 声明切点
@Pointcut("execution(* *.Hello())")
public void pointcut() {
}
@Before("pointcut()")
public void beforepoint() {
System.out.println("接下去调用Hello()......");
}
@AfterReturning("pointcut()")
public void afterhello() {
System.out.println("函数Hello()执行结束......");
}
}
bean类HelloWorld(@Configuration后)加上:
@ComponentScan
@EnableAspectJAutoProxy
TestHelloWorld类不变,运行结果如下 :
2017-11-20 00:23:05 [org.springframework.context.annotation.AnnotationConfigApplicationContext]-[INFO] Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@12bb4df8: startup date [Mon Nov 20 00:23:05 CST 2017]; root of context hierarchy
接下去调用Hello()......
Hello:How are you ?
函数Hello()执行结束......
spring的AOP机制
前言
在软件开发中,散布于应用中多处的功能被称为横切关注点,通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的。把这些横切关注点和业务逻辑分离出来正是AOP要解决的问题。AOP能够帮我们模块化横切关注点,换言之,横切关注点可以被描述为影响应用多出的功能。这些横切点被模块化特殊的类,这些类被称为切面。
术语定义
通知:切面有必须要完成的工作,在AOP中,切面的工作被称为通知。通知定义了切面是什么以及何时使用,除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题,它应该在某个方法之前?之后?之前和之后都调用?还是只在方法抛出异常时调用?
连接点:连接点是应用程序执行过程中,能够插入切面的一个点。
切点:是在连接点的基础上定义切点,比方说一个类由十几个方法,每个方法的调用前和调用后都可以插入通知,但是你只想选择几个方法插入通知,因此你定义一个切点来选择你想插入的通知的方法。
切面:切面就是通知和切点的结合。
织入:织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:编译期、类加载期、运行期。其中编译器织入需要特殊的编译器,类加载器织入需要特殊的类加载器,spring的AOP 是在运行期织入通知的。
Spring的AOP支持
spring提供了AOP的四种支持,分别是:基于代理的经典Spring AOP模式;纯POJO切面;@AspectJ注解驱动的切面;@注入式AspectJ切面。spring所创建的通知都是用标准的Java类编写的,而且定义通知所应用的切点通常会使用注解或在Spring配置文件里采用XML来编写。
spring只支持方法级别的连接点。
在spring AOP中,要使用AspectJ的切点表达式语言来定义切点,关于Spring AOP的AspectJ切点,最重要的一点就是Spring仅支持AspectJ切点指示器的一个子集:
1.arg() 限制连接点匹配参数为指定类型的执行方法;
2.@args() 限制连接点匹配参数由指定注解标注的执行方法;
3.execution() 用于匹配是连接点的执行方法;
4.this() 限制连接点匹配AOP代理的bean引用为指定类型的类
5.target 限制连接点匹配目标对象为指定类型的类
6.@target() 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解
7.within() 限制连接点匹配指定的类型
8.@within() 限制连接点匹配特定注解所标注的类型
9.@annotation 限定匹配带有指定注解的连接点
spring 注解创建切面
目标对象:
package concert;
public interface Performance{
public void perform();
}
切面对象:
package concert;
@Aspect//表示Audience的实例是一个切面
public class Audience{
@Before("execution(**concert.Performance.perform(..))")
public void silenceCellPhones(){
//在perfrom方法执行之前
}
@Before("execution(**concert.Performance.perform(..))")
public void takeSeats(){
//在perfrom方法执行之前
}
@AfterReturning("execution(**concert.Performance.perform(..))")
public void silenceCellPhones(){
//在perfrom方法执行之后
}
@AfterThrowing("execution(**concert.Performance.perform(..))")
public void silenceCellPhones(){
//在perfrom方法抛出异常之后
}
}
上面的类中切点表达式execution(**concert.Performance.perform(…))多次出现,我们也可以通过@Pointcut注解避免每次都写很长的切点表但是如下所示:
@Aspect//表示Audience的实例是一个切面
public class Audience{
@Pointcut("execution(**concert.Performance.perform(..))")
public void performance(){}
@Before("performance()")
public void silenceCellPhones(){
//在perfrom方法执行之前
}
@Before("performance()")
public void takeSeats(){
//在perfrom方法执行之前
}
@AfterReturning("performance()")
public void silenceCellPhones(){
//在perfrom方法执行之后
}
@AfterThrowing("performance()")
public void silenceCellPhones(){
//在perfrom方法抛出异常之后
}
}
接下来需要在配置文件中配置切面如下所示:
@Configuration
@EnableAspectJAutoProxy//启动AspectJ自动代理
@ComponentScan
public class ConcertConfig{
}
//或者在配置文件中配置中添加
<aop:aspectj-autoproxy />
表示启动切面代理
环绕通知:
@Aspect//表示Audience的实例是一个切面
public class Audience{
@Pointcut("execution(**concert.Performance.perform(..))")
public void performance(){}
@Before("performance()")
public void watchPerformance(ProceedingJoinPoint jp){
//在方法之前执行
System.out.println(" beform the method is invoked");
jp.proceed()//控制权交给目标方法
//在方法之后执行
System.out.println(" after the method is invoked");
}
}
处理通知中的参数
public class Audience{
@Pointcut("execution(**concert.Performance.perform(int))&&args(trackNumber)")
public void performance(){}
@Before("performance(trackNumber)")
public void watchPerformance(int trackNumber){
//截获传递给目标方法的参数并传递给切面中处理方法
System.out.println(trackNumber);
}
}
xml中声明切面
spring AOP提供的xml配置元素:
1.aop:advisor 定义AOP通知;
2.aop:after 后置通知;
3.aop:after-returning 返回通知
4.aop:around 环绕通知
5.aop:aspect 定义一个切面
6.aop:aspectj-autoproxy 启用切面注解驱动
7.aop:before 前置通知
8.aop:config 顶层的AOP配置元素;
9.aop:pointcut:定义个切点
<aop:config>
<aop:aspect ref="audience">
<aop:before
pointcut="execution(**concert.Performance.perform())" method="silenceCellPhones"/>
<aop:before
pointcut="execution(**concert.Performance.perform())" method="takeSeats"/>
<aop:after-returning
pointcut="execution(**concert.Performance.perform())" method="applause"/>
<aop:after-throwing
pointcut="execution(**concert.Performance.perform())" method="demandRefund"/>
</aop:aspect>
</aop config>
定义切点:
<aop:config>
<aop:aspect ref="audience">
<aop:pointcut id="performance" expression="execution(**concert.Performance.perform())">
<aop:before
pointcut-ref="performance" method="silenceCellPhones"/>
<aop:before
pointcut="performance" method="takeSeats"/>
<aop:after-returning
pointcut="performance" method="applause"/>
<aop:after-throwing
pointcut="performance" method="demandRefund"/>
</aop:aspect>
</aop config>