Spring学习笔记(二)注解&AOP

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.值类型的注入

  1. 通过反射的field赋值、但会破坏封装性
//例如,在bean对象中这样配置
		@Value("张三")
		private String name;
  1. 通过set方法赋值
//通过set方法赋值
		@Value("张三")
		public void setName(String name) {
			this.name = name;
		}

5.对象类型的注入

  1. 自动装配:@Autowired
	@Autowired//自动装配(需要把相应的Car对象也注解到容器)
	private Car car;
  1. 在自动装配的基础上使用@Qualifier注解告诉spring容器自动装配哪个名称的对象
	@Autowired//自动装配 
	@Qualifier("car2")//告诉spring容器自动装配哪个名称的对象
	private Car car;
  1. @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这是后置通知(出现异常也会调用)");
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值