spring的bean管理:注解
1.将对象注解到容器: @Component组件
Spring 中提供@Component 的三个衍生注解:(功能目前来讲是一致的)
- @Controller :WEB 层
- @Service :业务层
- @Repository :持久层
这三个注解是为了让标注类本身的用途清晰,Spring 在后续版本会对其增强
2.修改对象的作用范围:@Scope
- singleton:单例
- prototype:多例
3.生命周期的配置:@PostConstruct 、@PreDestroy
- @PostConstruct :相当于 init-method
- @PreDestroy :相当于 destroy-method
4.值类型的注入
- 通过反射的field赋值、但会破坏封装性
//例如,在bean对象中这样配置
@Value("张三")
private String name;
- 通过set方法赋值
//通过set方法赋值
@Value("张三")
public void setName(String name) {
this.name = name;
}
5.对象类型的注入
- 自动装配:@Autowired
@Autowired//自动装配(需要把相应的Car对象也注解到容器)
private Car car;
- 在自动装配的基础上使用@Qualifier注解告诉spring容器自动装配哪个名称的对象
@Autowired//自动装配
@Qualifier("car2")//告诉spring容器自动装配哪个名称的对象
private Car car;
- @Resource(name=“对象名字”)指定注入
@Resource(name="car3")
private Car car;
6.spring与Junit的整合测试
在测试类中添加注解
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
对比:使用整合测试前
@Test
public void fun1() {
//1.创建容器对象
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//2.向容器要User对象
User u=(User) ac.getBean("user");
//3.打印User对象
System.out.println(u);
ac.close();
}
对比:使用整合测试后
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
//将名为user对象注入到u变量中
@Resource(name="user")
private User u;
@Test
public void fun2() {
System.out.println(u);
}
}
7.案例
这是bean类,我们在bean类中使用注解将对象注解到容器中,并把值和对象也注入进去
@Component("user")//将对象注解到容器
@Scope(scopeName="singleton")//单例
public class User {
@Value("张三")
private String name;
private Integer age;
@Autowired//自动装配
@Qualifier("car2")//告诉spring容器自动装配哪个名称的对象
@Resource(name="car")
private Car car;
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
@Value("18")
public void setAge(Integer age) {
this.age = age;
}
@PostConstruct
public void init(){
System.out.println("我是初始化方法!");
}
@PreDestroy
public void destroy(){
System.out.println("我是销毁方法!");
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
@Component("car")
public class Car {
@Value("玛莎")
private String name;
@Value("blue")
private String color;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getColor() {return color;}
public void setColor(String color) {this.color = color;}
@Override
public String toString() {return "Car [name=" + name + ", color=" + color + "]";}
}
这是spring的xml配置,会去按要求扫描bean对象并完成注解。这里还额外注解了一个car2对象,区别于car
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<!-- 指定扫描这个包下面的所有类的注解
注意:扫描包时会扫描指定包下的所有子孙包
-->
<context:component-scan base-package="cn.itcast.bean"></context:component-scan>
<bean name="car2" class="cn.itcast.bean.Car">
<property name="name" value="布加迪"></property>
<property name="color" value="橘色"></property>
</bean>
</beans>
这是测试代码
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
//将名为user对象注入到u变量中
@Resource(name="user")
private User u;
@Test
public void fun1() {
//1.创建容器对象
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//2.向容器要User对象
User u=(User) ac.getBean("user");
//3.打印User对象
System.out.println(u);
ac.close();
}
@Test
public void fun2() {
System.out.println(u);
}
}
8.spring的bean管理方式对比
XML 和注解:
- XML :结构清晰.
- 注解 :开发方便.(属性注入.)
实际开发中还有一种 XML 和注解整合开发:
- Bean 有 XML 配置.但是使用的属性使用注解注入
spring的AOP
AOP简介
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译
方式和运行期间动态代理
实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
spring的AOP作用:
对程序进行增强,不修改源码的情况下,AOP 可以进行权限校验,日志记录,性能监控,事务控制
实现原理: Spring 的 AOP 的底层用到两种代理机制
- JDK 的动态代理 :针对实现了接口的类产生代理.
- Cglib 的动态代理 :针对没有实现接口的类产生代理. 应用的是底层的字节码增强的技术生成当前类
的子类对象
jdk的动态代理
//动态代理
public class UserServiceProxyFactory implements InvocationHandler {
public UserServiceProxyFactory(UserService us) {
super();
this.us = us;
}
private UserService us;
public UserService getUserServiceProxy(){
//生成动态代理
UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
this);
//返回
return usProxy;
}
@Override
public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
System.out.println("打开事务!");
Object invoke = method.invoke(us, arg2);
System.out.println("提交事务!");
return invoke;
}
}
Cglib的动态代理
//cglib代理
public class UserServiceProxyFactory2 implements MethodInterceptor {
public UserService getUserServiceProxy(){
Enhancer en = new Enhancer();//帮我们生成代理对象
en.setSuperclass(UserServiceImpl.class);//设置对谁进行代理
en.setCallback(this);//代理要做什么
UserService us = (UserService) en.create();//创建代理对象
return us;
}
@Override
public Object intercept(Object prxoyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
//打开事务
System.out.println("打开事务!");
//调用原有方法
Object returnValue = methodProxy.invokeSuper(prxoyobj, arg);
//提交事务
System.out.println("提交事务!");
return returnValue;
}
}
测试代码
public class Demo {
@Test
//动态代理
public void fun1(){
UserService us = new UserServiceImpl();
UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
UserService usProxy = factory.getUserServiceProxy();
usProxy.save();
//代理对象与被代理对象实现了相同的接口
//代理对象 与 被代理对象没有继承关系
System.out.println(usProxy instanceof UserServiceImpl );//false
}
@Test
//cglib
public void fun2(){
UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
UserService usProxy = factory.getUserServiceProxy();
usProxy.save();
//判断代理对象是否属于被代理对象类型
//代理对象继承了被代理对象=>true
System.out.println(usProxy instanceof UserServiceImpl );//true
}
}
AOP 的开发中的相关术语
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点.
- Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义.
- Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field.
- Target(目标对象):代理的目标对象
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装在期织入
- Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类
- Aspect(切面): 是切入点和通知(引介)的结合
springAOP编程:使用xml配置
通知类Advice
//通知类
public class MyAdvice {
//前置通知-目标方法运行之前
//后置通知-如果出现异常就不会调用,在目标方法运行之后调用
//环绕通知-在目标方法前后都调用
//异常拦截通知-如果异常就调用
//后置通知-不管异常与否都调用 在目标方法之后
public void before() {System.out.println("这是前置通知");}
public void afterReturning() {System.out.println("这是后置通知(如果出现异常不会调用)");}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("这是环绕通知之前的部分");
Object proceed = pjp.proceed();//调用目标方法
System.out.println("这是环绕通知之后的部分");
return proceed;
}
//异常通知
public void afterException() {System.out.println("出现异常了!!!");}
public void after() {System.out.println("这是后置通知(出现异常也会调用)");}
}
配置xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
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-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<!-- 准备工作:导入aop约束命名空间-->
<!--1.配置目标对象-->
<bean name="userService" class="cn.itcast.service.UserServiceImpl"></bean>
<!--2.配置通知对象-->
<bean name="myAdvice" class="cn.itcast.d_springaop.MyAdvice"></bean>
<!--3.配置将通知织入目标对象-->
<aop:config>
<!-- 配置切入点
public void cn.itcast.service.UserServiceImpl.save()
void cn.itcast.service.UserServiceImpl.save()
* cn.itcast.service.UserServiceImpl.save()
* cn.itcast.service.UserServiceImpl.*()
* cn.itcast.service.*ServiceImpl.*()
* cn.itcast.service..*ServiceImpl.*()
* cn.itcast.service..*ServiceImpl.*(..)
-->
<aop:pointcut expression="execution(* cn.itcast.service.*ServiceImpl.*(..))" id="pc"/>
<aop:aspect ref="myAdvice">
<!-- 指定名为before方法作为前置通知 -->
<aop:before method="before" pointcut-ref="pc"/>
<!-- 后置 -->
<aop:after-returning method="afterReturning" pointcut-ref="pc"/>
<!-- 环绕 -->
<aop:around method="around" pointcut-ref="pc"/>
<!-- 异常拦截 -->
<aop:after-throwing method="afterException" pointcut-ref="pc"/>
<!-- 后置 不管异常 -->
<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:cn/itcast/d_springaop/applicationContext.xml")
public class Demo {
@Resource(name="userService")
private UserService us;
@Test
public void fun1(){
us.delete();
}
}
这是前置通知
这是环绕通知之前的部分
删除用户!
这是后置通知(出现异常也会调用)
这是环绕通知之后的部分
这是后置通知(如果出现异常不会调用)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:cn/itcast/d_springaop/applicationContext.xml")
public class Demo {
@Resource(name="userService")
private UserService us;
@Test
public void fun1(){ us.save();}
}
这是前置通知
这是环绕通知之前的部分
保存用户!
这是后置通知(出现异常也会调用)
出现异常了!!!
springAOP编程:使用注解配置
通知类
//通知类
@Aspect//表示这是一个通知/增强类
public class MyAdvice {
@Before("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void before() {
System.out.println("这是前置通知");
}
@AfterReturning("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void afterReturning() {
System.out.println("这是后置通知(如果出现异常不会调用)");
}
@Around("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("这是环绕通知之前的部分");
Object proceed = pjp.proceed();//调用目标方法
System.out.println("这是环绕通知之后的部分");
return proceed;
}
//异常通知
@AfterThrowing("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void afterException() {
System.out.println("出现异常了!!!");
}
@After("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void after() {
System.out.println("这是后置通知(出现异常也会调用)");
}
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<bean name="userService" class="cn.itcast.service.UserServiceImpl"></bean>
<!-- <bean name="myAdvice" class="cn.itcast.e_annotation.MyAdvice"></bean> -->
<bean name="myAdvice2" class="cn.itcast.e_annotation.MyAdvice"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:cn/itcast/e_annotation/applicationContext.xml")
public class Demo {
@Resource(name="userService")
private UserService us;
@Test
public void fun1(){
us.delete();
}
}
这是环绕通知之前的部分
这是前置通知
删除用户!
这是环绕通知之后的部分
这是后置通知(出现异常也会调用)
这是后置通知(如果出现异常不会调用)
上面的advice类中,我们发现,每个通知都要写一个切点的表达式,比如@Before("execution(* cn.itcast.service.*ServiceImpl.*(..))")
,对此可以相办法改进
改进后的通知advice类
//通知类
@Aspect//表示这是一个通知/增强类
public class MyAdvice2 {
@Pointcut("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void pc() {}
@Before("MyAdvice2.pc()")
public void before() {
System.out.println("2这是前置通知");
}
@AfterReturning("MyAdvice2.pc()")
public void afterReturning() {
System.out.println("2这是后置通知(如果出现异常不会调用)");
}
@Around("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("2这是环绕通知之前的部分");
Object proceed = pjp.proceed();//调用目标方法
System.out.println("2这是环绕通知之后的部分");
return proceed;
}
//异常通知
@AfterThrowing("MyAdvice2.pc()")
public void afterException() {
System.out.println("2出现异常了!!!");
}
@After("MyAdvice2.pc()")
public void after() {
System.out.println("2这是后置通知(出现异常也会调用)");
}
}