使用注解配置Spring、Spring-Aop

使用注解配置Spring

导包

使用注解配置Spring需要导入 spring-aop-4.2.4.RELEASE.jar

导入约束

在主配置文件中导入新的命名空间,也就是导入约束

xmlns:context="http://www.springframework.org/schema/context" 

开启使用注解代替配置文件

<context:component-scan base-package=""></context:component-scan>

在主配置文件applicationContext.xml中,全部代码如下

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 3        xmlns="http://www.springframework.org/schema/beans" 
 4        xmlns:context="http://www.springframework.org/schema/context" 
 5        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 ">
 6 
 7     <!-- 
 8         指定扫描com.jinxin.bean包下所有类的注解
 9         注意:扫描包时会扫描该包下所有的子孙包
10     -->
11     <context:component-scan base-package="com.jinxin.bean"></context:component-scan>
12 
13 </beans>
完整配置

开始使用注解

将对象注册到容器

在要交给容器管理的类上面使用 @Component("name") 注解,就可以代替<bean>标签

考虑到区分不同的代码层也可以分别使用如下三个标签来注册

@Service("name")    // service层
@Controller("name") // web层
@Repository("name") // Dao层 

 效果同@Component("name")一致

例:

将UserInfo交给容器管理

 1 import org.springframework.stereotype.Component;
 2 
 3 @Component("userinfo")
 4 public class UserInfo {
 5     private String name;
 6     private Integer age;
 7     
 8     public String getName() {
 9         return name;
10     }
11     public void setName(String name) {
12         this.name = name;
13     }
14     public Integer getAge() {
15         return age;
16     }
17     public void setAge(Integer age) {
18         this.age = age;
19     }
20 }
UserInfo

测试

 1 import org.junit.Test;
 2 import org.springframework.context.support.ClassPathXmlApplicationContext;
 3 
 4 public class NewTest {
 5     @Test
 6     public void test() {
 7         ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
 8         UserInfo ui = (UserInfo) ac.getBean("userinfo");
 9         System.out.println(ui);
10     }
11 }
测试

修改对象的作用范围

使用 @Scope(ScopeName="property") 代替<bean>标签里面的scope属性为对象设置作用范围

例:

将UserInfo修改为多例

 1 import org.springframework.context.annotation.Scope;
 2 import org.springframework.stereotype.Component;
 3 
 4 @Component("userinfo")
 5 @Scope(scopeName="prototype")
 6 public class UserInfo {
 7     private String name;
 8     private Integer age;
 9     
10     public String getName() {
11         return name;
12     }
13     public void setName(String name) {
14         this.name = name;
15     }
16     public Integer getAge() {
17         return age;
18     }
19     public void setAge(Integer age) {
20         this.age = age;
21     }
22     
23 }
修改UserInfo为多例

测试

 1 import org.junit.Test;
 2 import org.springframework.context.support.ClassPathXmlApplicationContext;
 3 
 4 public class NewTest {
 5     @Test
 6     public void test() {
 7         ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
 8         UserInfo ui = (UserInfo) ac.getBean("userinfo");
 9         UserInfo ui1 = (UserInfo) ac.getBean("userinfo");
10         System.out.println(ui == ui1);
11     }
12 }
测试

值类型注入

值类型注入可以使用属性注入跟set方法注入(推荐)两种,其中set方法注入更加推荐

例:

 1 import org.springframework.beans.factory.annotation.Value;
 2 import org.springframework.context.annotation.Scope;
 3 import org.springframework.stereotype.Component;
 4 
 5 @Component("userinfo")
 6 @Scope(scopeName="prototype")
 7 public class UserInfo {
 8     @Value("timo")    // 属性注入
 9     private String name;
10     private Integer age;
11     
12     public String getName() {
13         return name;
14     }
15     @Value("taylor")     // set方法注入(推荐)
16     public void setName(String name) {
17         this.name = name;
18     }
19     public Integer getAge() {
20         return age;
21     }
22     public void setAge(Integer age) {
23         this.age = age;
24     }
25     
26 }
值类型注入&set方法注入

引用类型注入

引用类型注入分为两种,自动装配(@Autowired)跟手动装配(@Resource(name="girlfriend"))

例:

准备一个GirlFriend类作为UserInfo的引用类型属性

 1 import org.springframework.beans.factory.annotation.Value;
 2 import org.springframework.stereotype.Repository;
 3 
 4 @Repository("girlfriend")
 5 public class GirlFriend {
 6     private String name;
 7     private Integer age;
 8     
 9     public String getName() {
10         return name;
11     }
12     @Value("Scarlet")
13     public void setName(String name) {
14         this.name = name;
15     }
16     public Integer getAge() {
17         return age;
18     }
19     @Value("18")
20     public void setAge(Integer age) {
21         this.age = age;
22     }
23 }
GrilFriend

在UserInfo中为其注入值

 1 import javax.annotation.Resource;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.beans.factory.annotation.Value;
 5 import org.springframework.context.annotation.Scope;
 6 import org.springframework.stereotype.Component;
 7 
 8 @Component("userinfo")
 9 @Scope(scopeName="prototype")
10 public class UserInfo {
11     private String name;
12     private Integer age;
13     @Autowired      // 自动装配
14     @Resource(name="girlfriend")    // 手动装配(指定girlfriend为装配对象)
15     private GirlFriend girlFriend;
16     
17     public GirlFriend getGirlFriend() {
18         return girlFriend;
19     }
20     
21     public void setGirlFriend(GirlFriend girlFriend) {
22         this.girlFriend = girlFriend;
23     }
24     public String getName() {
25         return name;
26     }
27     public void setName(String name) {
28         this.name = name;
29     }
30     public Integer getAge() {
31         return age;
32     }
33     public void setAge(Integer age) {
34         this.age = age;
35     }
36     
37     @Override
38     public String toString() {
39         // TODO Auto-generated method stub
40         return girlFriend.getName() + "-->" + girlFriend.getAge();
41     }
42 }
引用类型注入

测试

 1 import org.junit.Test;
 2 import org.springframework.context.support.ClassPathXmlApplicationContext;
 3 
 4 public class NewTest {
 5     @Test
 6     public void test() {
 7         ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
 8         UserInfo ui = (UserInfo) ac.getBean("userinfo");
 9         System.out.println(ui);
10     }
11 }
测试

初始化&销毁方法

使用注解 @PostConstruct 跟 @PreDestory 指定初始化方法跟销毁方法

例:

@PostConstruct
public void start(){
    System.out.println("我是初始化方法");
}

@PreDestory
public void destory(){
    System.out.println("我是方法");
}

名词学习

  • 连接点:目标对象中,所有可以增强的方法
  • 切入点:目标对象中,已经增强的方法
  • 通知/增强:增强的代码
  • 目标对象:被代理的对象
  • 织入:将通知应用到切入点的过程
  • 代理:将通知织入到目标对象,形成代理对象
  • 切面:切入点+通知

Spring-aop

  • 前置通知:目标方法运行之前执行
  • 后置通知:目标方法之后运行,如果出现异常则不会调用
  • 环绕通知:在目标方法运行之前和之后都会调用
  • 异常拦截通知:如果出现异常就会调用
  • 后置通知:目标方法运行之后调用,无论是否出现异常都会调用

首先准备一个类作为通知类

这个类主要存放的是前置通知后置通知等执行的方法

import org.aspectj.lang.ProceedingJoinPoint;
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 proeed = pjp.proceed();
		System.out.println("这是环绕通知之后的部分");
		return proeed;
	}
	// 异常拦截通知
	public void afterException() {
		System.out.println("异常拦截通知");
	}
	// 后置通知
	public void after() {
		System.out.println("后置通知  (无论是否出现异常都会调用)");
	}
}

准备一个类作为被通知的类

这个类是我们需要执行的类,在执行类中的方法的时候会调用前置通知,后置通知,环绕通知等一系列通知

public class UserService {
	public void save() {
		System.out.println("保存方法");
	}
}

在主配置文件中进行配置

虽然已经有了通知类和被通知类,但是还需要在配置文件中进行配置指定被通知类的前置通知,后置通知等要执行方法

<?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 ">

	<!-- 
	    指定扫描com.jinxin.bean包下所有类的注解
        注意:扫描包时会扫描该包下所有的子孙包
    -->
	<context:component-scan base-package="com.jinxin.bean"></context:component-scan>

	<!-- 配置目标对象 -->
	<bean name="user" class="com.jinxin.bean.UserService"></bean>
	<!-- 配置通知对象 -->
	<bean name="myAdvice" class="com.jinxin.aop.MyAdvice"></bean>
	
	<aop:config>
		<!-- 配置切入点  
			public void com.jinxin.bean.UserService.save()
			void com.jinxin.bean.UserService.save()
			* com.jinxin.bean.UserService.*()
			
			* com.jinxin.bean.UserService.*(..)
			
			* com.jinxin.bean.*Service.*(..)
		-->
		<aop:pointcut expression="execution(* com.jinxin.bean.*Service.*(..))" id="pc"/>
		<aop:aspect ref="myAdvice">
			<!--指定名为before方法作为前置通知  -->
			<aop:before method="before" pointcut-ref="pc" />			
			<!--指定名为afterReturning方法作为后置通知  -->
			<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>

 在配置切入点的时候有一个小插曲,原本要配置的形态是这样 public void com.jinxin.bean.UserService.save() ,最后却变成了这样 * com.jinxin.bean.*Service.*(..) ,下面对这些*号进行一一的解释

public void   --->   *

将public void用*代替表示了可以通知任意类型的方法

UserService  --->  *Service

将UserService用*Service代替,表示可以通知任意xxxService形式的类

save  --->  *

将save方法替换成了*表示可以通知该类中所有的方法

()  --->  (..)

将()替换成(..)表示可以接受任意参数的方法

 

posted @ 2018-07-31 11:57  Jin同学  阅读(186)  评论(0)    收藏  举报