新注解说明
@configuration
- 用于指定当前类是一个 spring 配置类;
- 相当于spring.xml配置文件。
@ComponenScan
- 用于扫描注解;
- 相当于<context:component-scan base-package=“xxx”>标签
@Bean
- 该注解只能写在方法上,表明使用方法创建一个对象,并且放入 spring 容器;
- 相当于标签
@PropertySource
- 用于加载properties文件;
- 相当于<context:property-placeholder location=“xxx.properties”>标签
@Import
- 用于导入其他配置类;
- 相当于标签
Spring纯注解开发
- 开发步骤
- ①定义service层及其实现子类
- ②定义dao层及其实现子类
- ③定义MySpringCoreConfiguration
- ④定义MySpringDaoConfiguration
- ⑤代码测试
- ①定义service层及其实现子类
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> selectUserList() throws Exception {
return userDao.selectUserList();
}
}
②定义dao层及其实现子类
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private QueryRunner queryRunner;
@Override
public List<User> selectUserList() throws Exception {
return queryRunner.query(
"select user_id userId,user_name userName, user_pwd userPwd from tb_user",
new BeanListHandler<>(User.class)
);
}
}
③定义MySpringCoreConfiguration
@Configuration
@ComponentScan("com.atguigu")//扫描注解
@Import(MySpringDaoConfiguration.class)
public class MySpringCoreConfiguration {
}
④定义MySpringDaoConfiguration
@Configuration
@PropertySource("jdbc.properties")
public class MySpringDaoConfiguration {
@Value("${driverClassName}")
private String driverClassName ;
@Value("${url}")
private String url ;
@Value("${user}")
private String user ;
@Value("${password}")
private String password ;
@Bean
public QueryRunner getQueryRunner(DataSource ds){
return new QueryRunner(ds);
}
@Bean
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(password);
return dataSource;
}
}
⑤代码测试
public class UserController {
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MySpringCoreConfiguration.class);
UserService userService = applicationContext.getBean(UserService.class);
List<User> userList = userService.selectUserList();
System.out.println("userList = " + userList);
}
}```
```java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MySpringCoreConfiguration.class)
public class UserServiceTest {
@Autowired
private UserService userService ;
@Test
public void selectUserList() throws Exception {
List<User> userList = userService.selectUserList();
System.out.println("userList = " + userList);
}
}
注解开发的作用和弊端
总结
- 自定义类使用注解来处理
- 第三方类使用XML来处理
AOP
概述
- 概述
- AOP : aspect oritented programing : 面向切面编程
- AOP关注的是程序中的共性功能,开发时,将共性功能抽取出来制作成独立的功能模块,此 时原始功能中将不具有这些被抽取出的共性功能代码。在被抽取的共性功能的模块运行时 候,将共性功能模块也运行,即可完成原始的功能。
- 作用
- 在程序运行期间,不修改源码对已有方法进行增强。
- 好处
- 减少重复代码
- 提供程序效率
- 方便项目维护
AOP原理环境搭建
代码实现
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("权限校验");
System.out.println("UserServiceImpl addUser");
System.out.println("日志记录");
}
@Override
public void deleteUser() {
System.out.println("权限校验");
System.out.println("UserServiceImpl deleteUser");
System.out.println("日志记录");
}
@Override
public void updateUser() {
System.out.println("UserServiceImpl updateUser");
}
@Override
public void selectUserList() {
System.out.println("UserServiceImpl selectUserList");
}
}
存在问题
- 将主要功能和辅助功能写到一块,不方便程序维护,增加程序的耦合性;
- 不满足单一职责原则。
动态代理之JDKProxy
- 概述
- 基于接口的代理类实现方案
- 开发步骤
- ①定义UserServiceJDKProxy工具类
- 获取代理类对象
- ②代码测试
- ①定义UserServiceJDKProxy工具类
- ①定义UserServiceJDKProxy工具类
public class UserServiceJDKProxy {
/**
* 获取UserService(被代理类)对象的代理类对象
*
* @param userService : 被代理类对象
* @return
*/
public static UserService createUserServiceJDKProxy(UserService userService) {
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),//被代理类的类加载器
userService.getClass().getInterfaces(),//被代理类实现的所有接口
new InvocationHandler() {//执行增强 (执行被代理类的方法)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//Object proxy: 代理类对象
// Method method:被代理类的原始方法
// Object[] args:方法的实际参数
Object result = null;
if ("addUser".equals(method.getName()) || "deleteUser".equals(method.getName())) {
System.out.println("权限校验");
//执行被代理类的原始方法
result = method.invoke(userService, args);
System.out.println("日志记录");
} else {
result = method.invoke(userService, args);
}
return result;
}
}
);
return proxy;
}
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("UserServiceImpl addUser");
}
@Override
public void deleteUser() {
System.out.println("UserServiceImpl deleteUser");
}
@Override
public void updateUser() {
System.out.println("UserServiceImpl updateUser");
}
@Override
public void selectUserList() {
System.out.println("UserServiceImpl selectUserList");
}
}
②代码测试
public class UserServiceJDKProxyTest {
@Test
public void createUserServiceJDKProxy() {
//被代理类对象
UserService userService = new UserServiceImpl();
//获取代理类对象
UserService userServiceJDKProxy = UserServiceJDKProxy.createUserServiceJDKProxy(userService);
userServiceJDKProxy.addUser();
userServiceJDKProxy.deleteUser();
userServiceJDKProxy.updateUser();
userServiceJDKProxy.selectUserList();
}
}
存在问题
- 必须要有接口!!!
动态代理之CGLIB
- 概述
- 不限定是否具有接口,可以对任意操作进行增强
- 不需要原始被代理对象,动态创建新的代理对象
- 工作流程
- 开发步骤
- ①定义UserServiceCGLIBProxy类
- 获取代理类对象
- ②代码测试
- ①定义UserServiceCGLIBProxy类
- ①定义UserServiceCGLIBProxy类
/**
* 获取UserService代理类对象
*/
public class UserServiceCGLIBProxy {
/**
* 获取代理类对象
*
* @param clazz : 被代理类的字节码对象
* @return
*/
public static UserService createUserServiceCGLIBProxy(Class clazz) {
Enhancer enhancer = new Enhancer();
//被代理类是代理类的父类
enhancer.setSuperclass(clazz);
//增强方法
enhancer.setCallback(new MethodInterceptor() {//拦截被代理类的原始方法
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//Object o: 代理类对象
// Method method: 被代理类的原始方法
// Object[] args: 方法的实际参数
// MethodProxy methodProxy: 代理类的方法
//执行被代理类对象的原始方法
//Object result = method.invoke(o, args);//死路一条
//Object result = methodProxy.invoke(o, args);//死路二条
Object result = null;
if ("addUser".equals(method.getName()) || "deleteUser".equals(method.getName())) {
System.out.println("权限校验");
result = methodProxy.invokeSuper(o, args);//活路一条
System.out.println("日志记录");
} else {
result = methodProxy.invokeSuper(o, args);
}
return result;
}
});
return (UserService) enhancer.create();
}
}
②代码测试
public class UserServiceCGLIBProxyTest {
@Test
public void createUserServiceCGLIBProxy() {
UserService userService = UserServiceCGLIBProxy.createUserServiceCGLIBProxy(UserServiceImpl.class);
userService.addUser();
userService.deleteUser();
userService.updateUser();
userService.selectUserList();
}
}
AOP名词解释
- 连接点 : join point
- 被代理类中的所有方法
- 切入点: pointcut
- 被代理类中的具有共性功能的方法
- 通知: advice
- 将共性功能抽取成独立的方法
- 切面:aspect
- 通知和切入点的关系,比如:前置关系、后置关系、环绕关系等等
- 目标对象类
- 包含切入点方法的类
- 织入
- 将共性功能(通知)加入到原始方法(切入点)执行的过程
AOP入门案例
- 开发步骤
- ①引入相关依赖
- spring-core、spring-beans、spring-context、spring-expression、spring-jcl、spring-aop、spring-aspects等等
- ②制作目标对象类
- ③制作通知类
- ④编写spring-core.xml
- 配置切面
- ⑤代码测试
- ①引入相关依赖
- ①引入相关依赖
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<junit.version>4.13.2</junit.version>
<lombok.version>1.18.22</lombok.version>
<spring.version>5.3.13</spring.version>
<dbutils.version>1.7</dbutils.version>
<druid.version>1.2.8</druid.version>
<mysql.version>5.1.48</mysql.version>
</properties>
<dependencies>
<!--junit start-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!--junit end-->
<!--lombok start-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--lombok end-->
<!--spring start-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jcl</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--spring end-->
<!--jdbc start-->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>${dbutils.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--jdbc end-->
</dependencies>
②制作目标对象类
public class UserServiceImpl implements UserService {
@Override
public void addUser() {//切入点
System.out.println("UserServiceImpl addUser");
}
@Override
public void deleteUser() {//切入点
System.out.println("UserServiceImpl deleteUser");
}
@Override
public void updateUser() {//连接点
System.out.println("UserServiceImpl updateUser");
}
@Override
public void selectUserList() {//连接点
System.out.println("UserServiceImpl selectUserList");
}
}
③制作通知类
/**
* 通知类
*/
@Component
public class MyAdvice01 {
/**
* 通知
*/
public void checkPermission(){
System.out.println("权限校验");
}
/**
* 通知
*/
public void printLog(){
System.out.println("日志记录");
}
}
④编写spring-core.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: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 https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--扫描注解-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
<!--配置切面 : 通知和切入点关系-->
<aop:config>
<aop:aspect ref="myAdvice01">
<!--前置通知-->
<aop:before method="checkPermission" pointcut="execution(public void com.atguigu.service.impl.UserServiceImpl.addUser())"></aop:before>
<!--后置通知-->
<aop:after method="printLog" pointcut="execution(public void com.atguigu.service.impl.UserServiceImpl.addUser())"></aop:after>
</aop:aspect>
</aop:config>
</beans>
⑤代码测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-core.xml")
public class UserServiceImplTest {
@Autowired
private UserService userService;
@Test
public void addUser() {
userService.addUser();
}
@Test
public void deleteUser() {
userService.deleteUser();
}
@Test
public void updateUser() {
userService.updateUser();
}
@Test
public void selectUserList() {
userService.selectUserList();
}
}
切入点表达式
语法
execution(权限修饰符 返回值类型 包名.类名.方法名(形参类型1,形参类型2...))
execution(public void com.atguigu.service.impl.UserServiceImpl.addUser(int))
-- 权限修饰符可以省略
execution( void com.atguigu.service.impl.UserServiceImpl.addUser(int))
-- 不限定返回值类型
execution( * com.atguigu.service.impl.UserServiceImpl.addUser(int))
-- 不限定包
execution( * *..UserServiceImpl.addUser(int))
-- 不限定类
execution( * *..*ServiceImpl.addUser(int))
-- 不限定方法名
execution( * *..*ServiceImpl.*(int))
-- 不限定方法参数
execution( * *..*ServiceImpl.*(..))
-
- 权限修饰符可以省略
- '*'代表任意包名、任意类名、任意方法名、任意参数类型
- '.'代表任意一个包下
- '…'代表任意一个或多个包、任意一个或多个参数(类型任意)
切入点表达式抽取
代码实现
<aop:config>
<aop:aspect ref="myAdvice01">
<aop:pointcut id="p1" expression="execution( * *..*ServiceImpl.*(..))"/>
<!--前置通知-->
<aop:before method="checkPermission" pointcut-ref="p1"></aop:before>
<!--后置通知-->
<aop:after method="printLog" pointcut-ref="p1"></aop:after>
</aop:aspect>
</aop:config>
<aop:config>
<aop:pointcut id="p1" expression="execution( * *..*ServiceImpl.*(..))"/>
<aop:aspect ref="myAdvice01">
<!--后置通知-->
<aop:after method="printLog" pointcut-ref="p1"></aop:after>
</aop:aspect>
<aop:aspect ref="myAdvice01">
<!--前置通知-->
<aop:before method="checkPermission" pointcut-ref="p1"></aop:before>
<!--后置通知-->
</aop:aspect>
</aop:config>
<aop:config>
<aop:aspect ref="myAdvice01">
<!--前置通知-->
<aop:before method="checkPermission"
pointcut="execution( * *..*ServiceImpl.*(..))"></aop:before>
<!--后置通知-->
<aop:after method="printLog"
pointcut="execution( * *..*ServiceImpl.*(..))"></aop:after>
</aop:aspect>
</aop:config>
通知类别
通知类别
通知类别 | 说明 |
---|---|
before | 前置通知,在切入点之前执行 |
after-returning | 正常后置通知,在切入点之后执行,切入点没有异常才执行 |
after-throwing | 异常后置通知,在切入点之后执行,切入点有异常才执行 |
after | 后置通知,在切入点之后执行,切入点不管是否有异常都执行 |
around | 环绕通知,在切入点之前执行,在切入点之后执行 |
代码实现
/**
* 通知类别
*/
@Component
public class MyAdvice02 {
/**
* 前置通知 ok
*/
public void before(){
System.out.println("MyAdvice02 before");
}
/**
* 正常后置通知
*/
public void afterReturning(){
System.out.println("MyAdvice02 afterReturning");
}
/**
* 异常后置通知 ok
*/
public void afterThrowing(){
System.out.println("MyAdvice02 afterThrowing");
}
/**
* 后置通知 ok
*/
public void after(){
System.out.println("MyAdvice02 after");
}
/**
* 环绕通知 ok
*/
public void around(ProceedingJoinPoint pjp){
System.out.println("MyAdvice02 around之前");
//执行切入点
try {
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
System.out.println("MyAdvice02 around之后");
}
}
<aop:config>
<aop:aspect ref="myAdvice02">
<aop:pointcut id="p1" expression="execution(* *..*Service.*(..))"/>
<aop:before method="before" pointcut-ref="p1"></aop:before>
<aop:after-returning method="afterReturning" pointcut-ref="p1"></aop:after-returning>
<aop:after-throwing method="afterThrowing" pointcut-ref="p1"></aop:after-throwing>
<aop:after method="after" pointcut-ref="p1"></aop:after>
<aop:around method="around" pointcut-ref="p1"></aop:around>
</aop:aspect>
</aop:config>
around通知模拟其他通知
- 代码实现
@Component
public class MyAdvice03 {
/**
* 前置通知
*/
public void around2Before(ProceedingJoinPoint pjp){
System.out.println("MyAdvice03 before");
try {
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
* 正常后置通知
* @param pjp
*/
public void around2AfterReturning(ProceedingJoinPoint pjp){
try {
pjp.proceed();
//1
System.out.println("MyAdvice03 afterReturning");
} catch (Throwable e) {
e.printStackTrace();
//2
}
//3
}
/**
* 异常后置通知
* @param pjp
*/
public void around2AfterThrowing(ProceedingJoinPoint pjp){
try {
pjp.proceed();
//1
} catch (Throwable e) {
e.printStackTrace();
//2
System.out.println("MyAdvice03 afterThrowing");
throw new RuntimeException(e);
}
//3
}
/**
* 后置通知
* @param pjp
*/
public void around2After(ProceedingJoinPoint pjp){
try {
pjp.proceed();
//1
} catch (Throwable e) {
e.printStackTrace();
//2
throw new RuntimeException(e);
} finally {
//3
System.out.println("MyAdvice03 after");
}
}
}
通知获取切入点输入参数
- 分类
- ①around通知获取切入点输入参数
- ②非around通知获取切入点输入参数
- 代码实现
@Component
public class MyAdvice04 {
/**
* ①around通知获取切入点输入参数
*/
public void around(ProceedingJoinPoint pjp) {
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
try {
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
* ②非around通知获取切入点输入参数
* @param jp
*/
public void before(JoinPoint jp){
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}
}
通知获取切入点的返回值
- 概述
- before : 无法获取
- after-returning : 可以获取
- after-throwing : 无法获取
- after : 无法获取
- around : 可以获取
- 代码实现
@Component
public class MyAdvice05 {
public void afterReturning(String result){
System.out.println("MyAdvice05 afterReturning result : " + result);
}
public Object around(ProceedingJoinPoint pjp){
System.out.println("MyAdvice05 around之前");
try {
Object result = pjp.proceed();
System.out.println("MyAdvice05 around result = " + result);
return result;
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("MyAdvice05 around之后");
return null;
}
}
<aop:config>
<aop:aspect ref="myAdvice05">
<aop:pointcut id="p1" expression="execution(* *..*Service.*(..))"/>
<aop:around method="around" pointcut-ref="p1"></aop:around>
<aop:after-returning method="afterReturning" pointcut-ref="p1" returning="result"></aop:after-returning>
</aop:aspect>
</aop:config>
通知获取切入点的异常信息
- 概述
- before : 无法获取
- after-returning : 无法获取
- after-throwing : 可以获取
- after : 无法获取
- around : 可以获取
- 代码实现
@Component
public class MyAdvice06 {
public void afterThrowing(Exception e){
System.out.println("MyAdvice05 afterThrowing exception : " + e);
}
public void around(ProceedingJoinPoint pjp){
System.out.println("MyAdvice06 around之前");
try {
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
System.out.println("MyAdvice06 around exception : " + e);
throw new RuntimeException(e);
}
System.out.println("MyAdvice06 around之后");
}
}
<aop:config>
<aop:aspect ref="myAdvice06">
<aop:pointcut id="p1" expression="execution(* *..*Service.*(..))"/>
<aop:around method="around" pointcut-ref="p1"></aop:around>
<aop:after-throwing method="afterThrowing" pointcut-ref="p1" throwing="e"></aop:after-throwing>
</aop:aspect>
</aop:config>