前言
前面讲了如何使用配置文件实现控制反转和依赖注入。jar包里的类只能在配置文件中进行配置,但自己写的类可以使用注解来完成配置。下面就讲一下如何使用注解实现控制反转和依赖注入。并且开始学习AOP面向切面编程。
一、使用注解完成IOC配置
使用注解将类转为bean组件,并将其添加到spring容器中。
1. 添加配置文件
spring配置文件增加context命名空间与xsd的引用,使用注解,需要context组件解析配置文件。
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--使用注解的方式为类配置成spring的bean组件,前提是需要启动扫描写注解的包-->
<!--通过扫描base-Package下的包,将包下的组件添加到spring容器中-->
<context:component-scan base-package="com"></context:component-scan>
</beans>
2. 将类转为bean组件的四种注解
这四种注解都可以将类变为bean组件,由spring容器创建和维护。区别在于这四种方式用来标识不同的层。
注解 | 作用 |
---|---|
@Repository | 用来标注持久层 |
@Service | 用来标注业务逻辑层 |
@Controller | 用来标注控制层 |
@Component | 如果不确定是哪一层,就用这个注解来标注 |
如果不在注解中注入id,则默认值为当前类首字母小写
3. Component注解
3.1 Component设置id
说明创建了Dog对象,转为spring组件后,由spring容器来创建和管理对象。
3.2 Component不设置id
不写id。默认id就是类名,首字母小写。
说明创建了Boy对象。
3.3 测试完整代码
public class DogTest {
//这里创建静态容器对象
//这个是spring对象,这个对象不能调用close方法
//private static ApplicationContext ac = null;
//这个是spring实现类对象,可以调用close方法关闭容器
private static ClassPathXmlApplicationContext ac = null;
static {
ac = new ClassPathXmlApplicationContext("applicationContext.xml");
}
/*使用注解*/
@Test
public void testDog01() {
Dog dog = (Dog) ac.getBean("dog");
System.out.println(dog);
}
@Test
public void testBoy02() {
Boy boy = (Boy) ac.getBean("boy");
System.out.println(boy);
}
}
二. 使用注解在类里实现依赖注入
依赖注入,实现为属性赋值。
1. 基本数据类型+其包装类+String
注解 | 作用 |
---|---|
@Value(“属性值”) | 为属性注入值 |
2. 对象
注解 | 作用 |
---|---|
@Resource(name = “dog”) | 相当于ref引入外部bean,给对象赋值 |
@Autowired | 按对象的数据类型自动注入(用的比较多) |
三、使用properties文件实现注入
依赖注入一般不直接写在类里,而是写在properties文件呢里。这样方便对属性的管理。
1. 在添加properties文件之前先设置一下编码
2. 创建properties文件
这个是JavaEE项目。在src下新建properties文件。
3. msg.properties
map和properties集合使用的是键值对,采用JSON来写。
属性文件代码:
dogname=小黑
dogage=3
boyname=小敏
boyage=20
jobs=学生,程序员
hobbies=游泳,跑步
language={'zh':'中文','en':'English'}
4. spring如何获取properties文件里的属性
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--使用注解的方式为类配置成spring的bean组件,前提是需要启动扫描写注解的包-->
<!--扫描包下的组件,将其添加到spring容器中-->
<context:component-scan base-package="com"></context:component-scan>
<!--引入properties文件共spring使用-->
<context:property-placeholder location="msg.properties"></context:property-placeholder>
</beans>
5. 使用${}表达式注入值
5.1 基本数据类型+其包装类+String
5.2 对象
5.3 array、list、set
array:
list、set:需要用split将字符串通过逗号分隔为数组,然后用ognl表达式做数据类型转换
5.4 map、properties集合
需要用#{}做数据类型转换。如果出现错误,在注解没有写错的情况下,可以看一下properties文件里的键值是不是写错了。
第二部分内容:AOP面向切面编程
一、AOP面向切面编程
1. 什么是AOP
AOP(aspect oriented programming)
如果有一些功能方法(切入点),他们如果具有相同的功能。可以将通用的功能单独写成一个方法(切面)。通过AOP可以实现当切入点执行时,切面程序会植入到切入点方法里的某个位置(通知)执行,这个位置叫做通知。这就是面向切面编程。
2. AOP的优势
AOP好处:对一些具有通用功能的业务方法进行统一处理(由切面统一处理)
3. 通知的种类
组件是spring管理的一个bean对象,bean里的方法才可以作为切入点
。
3.1 前置通知
切面在目标组件的切入点正确执行之前先执行。
3.2 后置通知
切面在目标组件的切入点正确执行之后先执行。
3.3 异常通知
切面在目标组件的切入点发生异常时执行。
3.4 最终通知
不管目标组件的切入点是否能正确执行,切面都一定会执行。
二、AOP编程
1. 新建JavaSE项目,导入jar包
2. applicationContext.xml
添加配置文件
①扫描包,并将包下的bean组件添加到spring容器中
②启动aspectj的自动代理后,才可以将该切面添加到spring容器中
<?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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启自动扫描,扫描base-package指定的包下的类,如果是bean组件,就将其注入到spring容器中 -->
<context:component-scan base-package="com"></context:component-scan>
<!-- spring中使用aspectj包中的@Aspect注解标注当前组件为切面,
如果要使用该注解必须开启aspectj的自动代理模式 -->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
</beans>
3. 前置通知
不管是切入点还是切面都是spring容器里的组件里的方法,所以要设置切入点或切面首先要将其设置为bean组件。
如果要设置切面的话还需要 @Aspect
,该注解让类里的方法可以设置为切面。
这里只是简单的模拟一下使用面向切面编程。
3.1 DeptDao
自定义切入点方法。
package com.dao;
import org.springframework.stereotype.Repository;
@Repository
public class DeptDao {
public Integer insert(String dname) {
System.out.println(dname + "插入成功!");
return 1;
}
public Integer update(String dname) {
System.out.println(dname + "修改成功!");
return 1;
}
}
3.2 DmlAspect
该文件用来写切面程序。
含义/作用 | |
---|---|
@Component | 将该类添加到spring容器里,设置为bean组件; |
@Aspect | 允许将类里的方法设置为切面; |
@Before | 将类里的方法设置为前置通知 |
execution | 切入点表达式,用来指定切入点 |
JoinPoint | 切入点对象 |
package com.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class DmlAspect {
// 前置通知的切面方法
@Before("execution(* com.dao.*.*(*))") //在注解里写切入点表达式
public void doBefore(JoinPoint point) {
System.out.println("**************前置通知**************");
System.out.println("拦截切入点的第一个参数:" + point.getArgs()[0]);
System.out.println("切入点方法原型:" + point.getSignature());
System.out.println("切入点所属对象:" + point.getTarget());
System.out.println("**********************************");
}
}
切入点方法的参数类型和数目都不一定:
在注解里写切入点表达式,符合该表达式的类里的方法都可以作为切入点。
execution是切入点表达式,用来指定切入点。切入点的返回值类型、包名、类名、方法名、参数数目和类型。(重点:后面会详解)
3.3 DeptTest
①创建spring容器
②通过spring容器获取bean组件对象
③通过bean组件来调用方法,执行切入点
④切入点在执行时,切面程序会监测到切入点的执行
package com.test;
import com.dao.DeptDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DeptTest {
private static ApplicationContext as = null;
static {
as = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void testDept() {
DeptDao deptDao = (DeptDao) as.getBean("deptDao");
deptDao.insert("开发部");
}
}
4. 后置通知
4.1 DmlAspect
含义/作用 | |
---|---|
@AfterReturning | 将方法设置为前置通知 |
pointcut | 用来指定可以运行的切入点 |
returning | 用来获取切入点的返回值 |
// 后置通知的切面方法,它是在切入点成功执行之后才会执行该切面方法
@AfterReturning(pointcut = "execution(* com.dao.*.*(*))", returning = "result")
public void afterReturning(JoinPoint point, Object result) {
System.out.println("**************后置通知******************");
System.out.println("拦截切入点的第一个参数:" + point.getArgs()[0]);
System.out.println("切入点方法原型:" + point.getSignature());
System.out.println("切入点所属对象:" + point.getTarget());
System.out.println("*********************************");
}
这里需要注意的是如果目标组件(切入点)发生异常,后置通知就不会执行。
总结
以上就是今天要讲的内容,本文仅仅简单介绍了如何使用注解创建bean对象,以及实现依赖注入。还讲解了什么是AOP面向切面编程,以及前置插入和后置插入。