一、AOP 切面编程介绍
在软件业,面向切面编程,是指通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP的特点:
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码。
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。
经典应用:
事务管理、性能监视、安全检查、缓存 、日志等
这些系统服务通常被称为横切关注点。
想象一下如果每个组件都单独去实现这些系统功能:
改变这些关注点的逻辑,修改各个模块当中的实现,方法的调用就会重复出现在各个模块中,组件会因为那些与自身核心业务无关的代码而变得混乱。
未建立切面
建立切面
对比
AOP编程术语
理解
二、AOP实战
1.建立切面
aop底层将采用代理机制进行实现。
动态代理的两种机制
A:
接口 + 实现类 :spring采用 jdk 的动态代理Proxy。
怎么做的?基于接口去实现。
缺点:如果目标类没有实现任何接口呢?jkd动态代理就无能为力。
B:
实现类:spring 采用 cglib字节码增强。
怎么做的?基于继承。也可以实现动态代理。
3.AOP实现的三种方式
- 手动方式(自己实现)
JDK动态代理
CGLib动态代理 - 半自动方式(SpringAOP了解),需要手动一个一个地根据委托类生成代理对象。
- 自动方式(AspectJ掌握),AspectJ帮助我们批量生成由委托类生成的代理对象。
2.1半自动方式SpringAOP
2.1.1.配置环境
在pom.xml中引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.8.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
2.1.2.代码实现
接口和实现类
public interface UserService {
public void sayHello();
}
@Service
public class UserServiceImpl implements UserService{
@Override
public void sayHello() {
System.out.println("hello qyt");
}
}
CustomInterceptor
@Component
public class CustomInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("有人说你野心勃勃");
Object proceed = methodInvocation.proceed();
System.out.println("有人爱你灵魂有火");
return proceed;
}
}
application.xml
<context:component-scan base-package="com.qyt"/>
<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--target:委托类-->
<property name="target" ref="userServiceImpl"/>
<!--interceptor组件的id,set接收的是字符串,这里写的是value属性-->
<property name="interceptorNames" value="customInterceptor"/>
</bean>
2.1.3.单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class SpringAopTest {
/*//写法一
@Autowired
@Qualifier("userServiceProxy")
UserService userService;*/
/*//写法二
@Resource(name = "userServiceProxy")
UserService userService;*/
//写法三
@Autowired
UserService userServiceProxy;
@Test
public void mytest1(){
userServiceProxy.sayHello();
}
}
2.1.4.这种方式的不足之处
每一个组件都需要单独使用ProxyFactoryBean生成代理组件,并且每次取出需要指定组件id。
2.2 自动方式AspectJ
AspectJ是什么?
AspectJ是基于java语言的AOP框架。
Spring2.0之后支持了基于AspectJ的切入点表达式的支持。
AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入
允许直接在class里面定义切面类。
主要用途,不改动现有代码的前提下,自定义开发。植入代码。
更侧重于指定方法 → 更侧重于划分方法的增强范围。
2.2.1.准备:引入依赖aspectjweaver
2.2.2 准备:application配置文件的aop标签
aop标签的获取:
1、使用已有的 2、去appendix找到 3、创建模板 4、自己改造
aop标签的使用:
切入点表达式
execution(修饰符 返回值 包名.类名.方法名(形参))
分析角度:
1、能否省略
2、能否通配
3、特殊用法
-
修饰符
可以省略不写 → 省略表示任意修饰符,一种特殊表达 -
返回值
不能省略
可以使用 * 来通配
如果是基本类型、包装类、java.lang目录下的类,可以直接写,javabean这些要写全类名,比如com.xxx.bean.User -
包名\类名\方法名
可以部分省略:除了头和尾的部分不能省略,中间的任意一部分都可以省略 → 使用…来省略
可以使用*来通配:可以表达一个层级或一个层级的一部分 -
形参
可以省略:表示无参方法
能否通配:
可以使用 * 来通配,一个 * 代表的是单个任意参数
多个参数使用多个 * ,逗号分隔
可以使用…代表任意数量的任意类型的参数
特殊用法:和返回值类似,javabean要写全类名 -
代码
<aop:config>
<!--id是一个标识,expression写的是切入点表达式,通过切入点表达式指定增强范围-->
<!--修饰符:可以省略不写代表任意修饰符-->
<!--返回值:不能省略,可以使用*通配,javabean要写全类名-->
<!--包名、类名、方法名:部分省略,头和尾不能省略,中间部分都可以使用..来省略-->
<aop:pointcut id="customPointcut1" expression="execution(public void com.cskaoyan.service.UserServiceImpl.say*())"/>
<!--中间的任意一部分都可以省略掉-->
<aop:pointcut id="customPointcut2" expression="execution(* com..say*())"/>
<aop:pointcut id="customPointcut3" expression="execution(* com..service..say*())"/>
<!--可以使用*通配一个层级或一个层级的一部分-->
<aop:pointcut id="customPointcut4" expression="execution(public void *.cskaoyan.service.UserServiceImpl.say*())"/>
<!--返回值:省略不写代表无参方法,使用*来通配单个任意类型的参数-->
<aop:pointcut id="customPointcut5" expression="execution(public void *.cskaoyan.service.UserServiceImpl.say*(*))"/>
<!--多个参数可以使用*,*,或者指定为对应类型-->
<aop:pointcut id="customPointcut6" expression="execution(public void *.cskaoyan.service.UserServiceImpl.say*(String,Integer))"/>
<!--使用..代表任意数量的任意类型的参数-->
<aop:pointcut id="customPointcut7" expression="execution(public void *.cskaoyan.service.UserServiceImpl.say*(..))"/>
<!--javabean要写全类名-->
<aop:pointcut id="customPointcut8" expression="execution(public void *.cskaoyan.service.UserServiceImpl.say*(com.cskaoyan.bean.User,String))"/>
<!--如果我们想要对service层的全部方法都做增强-->
<aop:pointcut id="servicePointcut" expression="execution(* com..service..*(..))"/>
</aop:config>
2.2.3 advisor(AspectJ实现方式一)
自定义通知通过实现接口,implements MethodInterceptor。
4.1.自定义通知
@Component
public class CustomInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("有人说你野心勃勃");
Object proceed = methodInvocation.proceed();
System.out.println("有人爱你灵魂有火");
return proceed;
}
}
4.2.advisor配置
<aop:config>
<!--id是一个标识,expression写的是切入点表达式,通过切入点表达式指定增强范围-->
<aop:pointcut id="customPointcut" expression="execution(public void com.cskaoyan.service.UserServiceImpl.say*())"/>
<!--advice-ref引用容器中的通知组件的id-->
<!--pointcut → advisor自己来写切入点表达式-->
<!--pointcut-ref → 引用配置过的aop:pointcut的对应id的切入点表达式-->
<aop:advisor advice-ref="customInterceptor" pointcut-ref="customPointcut"/>
</aop:config>
4.3.单元测试
接口和实现类
public interface UserService {
public void sayHello();
public void sayGoodbye();
}
@Service
public class UserServiceImpl implements UserService{
@Override
public void sayHello() {
System.out.println("hello qyt");
}
@Override
public void sayGoodbye() {
System.out.println("再见了");
}
}
单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class MyTest {
@Autowired
UserService userService;
@Test
public void mytest1(){
userService.sayHello();
}
@Test
public void mytest2(){
userService.sayGoodbye();
}
}
2.2.4 aspect(AspectJ实现方式二)
提供一些定义好的通知 → 相对于委托类的方法的时间不同
5.1.通知
5.2.定义切面类
5.2.1.joinPoint
5.2.2.通知
5.2.2.1.before
5.2.2.2.after
5.2.2.3.around
5.2.2.4.after-returning
5.2.2.5.after-throwing
5.3.使用注解的形式来使用aspectj
5.3.1.打开注解开关
5.3.2.切面类组件
5.3.2.1.定义为切面类
5.3.2.2.定义切入点表达式
方法的形式存在 →
表达id:方法名
表达expression:@Pointcut注解的value
方法体和形参里不需要写内容
5.3.2.3.通知方法
5.4.使用自定义的注解指定增强方法
Pointcut → 之前使用表达式指定增强方法 → 现在改为自定义注解
想要计算方法的执行时间 → 自定义注解加在哪一个方法上,哪一个就计算时间
5.4.1.自定义注解
5.4.2.Pointcut中使用自定义注解
5.5.小结
是否是所有的通知,都使用呢? 具体的业务使用具体的通知。
顺序:关注的是每个通知和委托类方法之间的顺序。
方法:切入点 → 指定增强方法 → 容器中组件中的方法。
三、JDBCTemplate
JDBCTemplate
Spring提供的用于操作jdbc的工具类。类似于JdbcUtils. 是对操作数据库的语句进行模板化的封装,让开发者只关注于数据库的sql语句即可。
JDBCTemplate也要依赖于数据源。
1.导包
2.se代码使用JdbcTemplate
3.Spring整合
spring-context
spring-test
4.补充一个autowired注解的使用
加载方法上 → 在实例的实例化过程中会调用到该方法
JdbcDaoSupport的拓展