Java学习笔记-Day66 Spring 框架(四)
一、基于注解的Spring AOP配置
Spring除了支持Schema(XML)方式配置AOP,还支持注解方式(使用@AspectJ风格的切面声明)。
Spring默认不支持@AspectJ风格的切面声明,为了支持需要在spring全局的配置文件中声明autoproxy。
1、操作步骤
(1)在spring全局的配置文件中需要声明autoproxy。
<!-- 告知spring,此时使用注解配置了和aop有关的切面类、前置通知、后置通知等等 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(2)在切面类上增加注解,在切面类的方法上增加通知类型和切入点。
- @Aspect:定义这个类为切面类。
- @Before:前置通知,切入点的逻辑(Advice),execution(…) 为切入点语法。
- @After:后置通知,切入点的逻辑(Advice),execution(…) 为切入点语法。
- @Around:环绕通知,切入点的逻辑(Advice),execution(…) 为切入点语法。
- @Component:作为切面类需要Spring管理起来,所以在初始化时就需要将这个类初始化加入Spring的管理。。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
// 切面类
@Aspect
public class LogAop {
// 前置通知的方法
@Before(value="execution(* com.etc.aop.service.impl.*.*(..))")
public void before(){
}
// 后置通知的方法
@After(value="execution(* com.etc.aop.service.impl.*.*(..))")
public void after(){
}
// 环绕通知的方法
@Around(value="execution(* com.etc.aop.service.impl.*.*(..))")
public Object around(ProceedingJoinPoint pjp) {
return null;
}
}
(3)测试类
@SpringJUnitConfig(locations="classpath:springaop.xml")
public class TestAop {
@Autowired
UsersService us;
@Test
public void test() {
Users u = us.login("tom", "1234");
System.out.println("users:"+u);
}
}
2、实现案例(用户登录的日志记录)
(1)Users实体类。
package com.etc.aop.entity;
public class Users {
private int userid;
private String username;
private String userpwd;
public Users(int userid, String username, String userpwd) {
super();
this.userid = userid;
this.username = username;
this.userpwd = userpwd;
}
public Users() {
super();
}
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserpwd() {
return userpwd;
}
public void setUserpwd(String userpwd) {
this.userpwd = userpwd;
}
@Override
public String toString() {
return "Users [userid=" + userid + ", username=" + username + ", userpwd=" + userpwd + "]";
}
}
(2)UsersDao接口。
package com.etc.aop.dao;
import com.etc.aop.entity.Users;
public interface UsersDao {
public Users getUsers(String username,String userpwd);
}
(3)UsersDao的实现类UsersDaoImpl。
package com.etc.aop.dao.impl;
import com.etc.aop.dao.UsersDao;
import com.etc.aop.entity.Users;
public class UsersDaoImpl implements UsersDao {
@Override
public Users getUsers(String username, String userpwd) {
if("tom".equals(username) && "1234".equals(userpwd)) {
return new Users(1,username,userpwd);
}
return null;
}
}
(4)UsersService接口。
package com.etc.aop.service;
import com.etc.aop.entity.Users;
public interface UsersService {
public Users login(String username,String userpwd);
}
(5)UsersService的实现类UsersServiceImpl。
package com.etc.aop.service.impl;
import com.etc.aop.dao.UsersDao;
import com.etc.aop.entity.Users;
import com.etc.aop.service.UsersService;
public class UsersServiceImpl implements UsersService {
private UsersDao usersdao;
public UsersDao getUsersdao() {
return usersdao;
}
public void setUsersdao(UsersDao usersdao) {
this.usersdao = usersdao;
}
@Override
public Users login(String username, String userpwd) {
return usersdao.getUsers(username, userpwd);
}
}
(6)LogAop切面类。
package com.etc.aop.log;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import com.etc.aop.entity.Users;
@Aspect
public class LogAop {
@Around(value="execution(* com.etc.aop.service.impl.*.*(..))")
public Object writeLog(ProceedingJoinPoint pjp) throws Throwable {
Object obj = pjp.proceed();
FileWriter fw = new FileWriter("log.txt",true);
//将Date类对象格式化成字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss ");
String dateStr = sdf.format(new Date());
if(obj!=null) {
Users users = (Users)obj;
fw.append(dateStr+"--"+users.getUsername()+"-- 登录成功\r\n");
}
fw.close();
return obj;
}
}
(7)Spring全局配置文件:springaop.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: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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean class="com.etc.aop.log.LogAop"></bean>
<bean class="com.etc.aop.dao.impl.UsersDaoImpl"></bean>
<bean class="com.etc.aop.service.impl.UsersServiceImpl" autowire="byType"></bean>
</beans>
(8)测试类
package com.etc.aop.test;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.etc.aop.entity.Users;
import com.etc.aop.service.UsersService;
@SpringJUnitConfig(locations="classpath:springaop.xml")
public class TestAop {
@Autowired
UsersService us;
@Test
public void test() {
Users u = us.login("tom", "1234");
System.out.println("users:"+u);
}
}
二、Spring Annotation注解
1、注解实现Bean依赖注入
注解实现Bean配置主要用来进行如依赖注入、生命周期回调方法定义等,不能消除XML文件中的Bean元数据定义,且基于XML配置中的依赖注入的数据将覆盖基于注解配置中的依赖注入的数据。
Spring3开始支持的基于注解实现Bean依赖注入,支持如下注解:
-
Spring自带依赖注入注解: Spring自带的一套依赖注入注解。
-
JSR-250注解:Java平台的公共注解,是Java EE 5规范之一,在JDK6中默认包含这些注解,从Spring2.5开始支持。JCP:Java Community Process是由多个厂家出人来构成的J2EE组织,主要是用于定Java的一些新的标准,而每一个标签都可以称之为一个JSR(Java Specification Requests),即 Java 规范提案。
-
JSR-330注解:Java 依赖注入标准,Java EE 6规范之一,可能在加入到未来JDK版本,从Spring3开始支持。
-
JPA注解:用于注入持久化上下文和实体管理器。
这三种类型的注解在Spring3中都支持,类似于注解事务支持,想要使用这些注解需要在Spring容器中开启注解驱动支持,在Spring全局配置文件使用如下配置方式开启:
<!-- 上下文支持注解的形式 -->
<context:annotation-config></context:annotation-config>
2、Spring自带依赖注入注解
(1)@Component:@Component注解在类上使用,表示该类定义为Spring管理的Bean,使用默认value(可选)属性表示Bean标识符。
(2)@Repository:@Component扩展,被@Repository注解的POJO类表示DAO层实现,使用方式和@Component相同。
(3)@Service:@Component扩展,被@Service注解的POJO类表示Service层实现,使用方式和@Component相同。
(4)@Controller:@Component扩展,被@Controller注解的类表示Web层实现,使用方式和@Component相同,放在Action前。
(5)@Value:主要用于普通字面量的注入,用于将值注入变量和方法参数。@Value放在Setter方法前,则会调用set方法进行Setter注入。@Value放在成员变量前,则会通过反射进行属性注入。
(6)@Autowired:主要用于对象的注入,属性注入不会调用set方法,如果@Autowired注解标注在set方法前,则会调用set方法进行Setter注入。@Autowired注解默认根据类型注入,如果有多个类型相同,可以使用@Autowired+@Qualifier组合,用@Qualifier指定名称。
注意:POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。
3、JSR-250注解
(1)@Resource:自动装配,主要用于对象的注入,如果指定name属性将根据名字装配,可以使用如下方式来指定:
- @Resource注解应该只用于setter方法注入,不能提供如@Autowired多参数方法注入。
- @Resource在没有指定name属性的情况下首先将根据setter方法对应的字段名查找资源,如果找不到再根据类型查找。
- @Resource首先将从JNDI环境中查找资源,如果没找到默认再到Spring容器中查找,因此如果JNDI环境中有和Spring容器同名的资源时需要注意。
(2)@PostConstruct:通过注解指定初始化方法定义。
(3)@PreDestroy:通过注解指定销毁方法定义。
4、JSR-330注解
@Named:等同于@Component注解。
@Inject:等同于@Autowired注解,使用时需要导入javax.inject-1.jar包。
5、注解对应的jar包
注解 | 对应jar包 |
---|---|
@Autowired | org.springframework.beans.factory.annotation.Autowired |
@Qualifier | org.springframework.beans.factory.annotation.Qualifier |
@Component | org.springframework.stereotype.Component |
@Resource | javax.annotation.Resource |
@Scope | org.springframework.context.annotation.Scope |
@PostConstruct | javax.annotation.PreDestroy |
@PreDestroy | javax.annotation.PreDestroy |
6、@Autowired和@Resource的区别
(1)@Autowired和@Resource所在的包不同。
(2)@Resource默认是按照name来装配的,如果没有name,再按照类型装配。而@Autowired默认使用类型装配,如果使用时指定名字,则需要配合Qualifier使用。
三、注解实现Bean依赖注入的操作步骤
(1)将applicationContext.xml配置文件的头部加入和注解有关的命名空间等信息。通过NameSpaces -> context打勾。
在Spring全局配置文件开启注解驱动支持。
<context:annotation-config></context:annotation-config>
这样当spring加载配置文件时,发现有<context:annotation-config/>
标签后,会加载以下四个类(用于处理annotation方式的配置):
- AutowiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- PersistenceAnnotationBeanPostProcessor
- RequiredAnnotationBeanPostProcessor
(2)配置文件中关于bean的配置信息需要转移到实体类、dao、service等进行配置,配置文件中通常要说明这些配置信息(注解)的位置。
<?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-4.3.xsd">
<!-- 上下文注解支持 -->
<context:annotation-config></context:annotation-config>
<!-- 配置扫描包位置 -->
<context:component-scan base-package="com.etc"></context:component-scan>
</beans>
(3)定义一个实体类,同时通过设置其注解表示形式。
package com.etc.entity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value="u")
public class Users {
@Value("1")
private int userid;
@Value("tom")
private String username;
@Value("123456")
private String userpwd;
public Users(int userid, String username, String userpwd) {
super();
this.userid = userid;
this.username = username;
this.userpwd = userpwd;
}
public Users() {
super();
}
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserpwd() {
return userpwd;
}
public void setUserpwd(String userpwd) {
this.userpwd = userpwd;
}
@Override
public String toString() {
return "Users [userid=" + userid + ", username=" + username + ", userpwd=" + userpwd + "]";
}
}
(4)定义一个Dao类,同时通过设置其注解表示形式。
package com.etc.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.etc.entity.Users;
@Repository(value="ud")
public class UsersDao {
//@Resource(name="u")
@Autowired
private Users u;
public void getUsers() {
System.out.println(u);
}
}
(5)测试类
package com.etc.test;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.etc.dao.UsersDao;
@SpringJUnitConfig(locations="classpath:applicationContext.xml")
public class TestSpringAnnotation {
@Autowired
private UsersDao ud;
@Test
public void test() {
ud.getUsers();
}
}
四、@Configuration 和 @Bean
Spring的新Java配置支持中的中心工件是 @Configuration注解类和@Bean注解方法。
使用@Configuration注解类和@Bean注解方法可以定义多个相同类型的Bean。
@Bean注解被用于指示一个方法实例,配置和初始化为通过Spring IOC容器进行管理的新对象。对于那些熟悉Spring的<beans/>
XML配置的人来说,@Bean注释与<bean/>
元素扮演的角色相同。你可以@Bean在任何Spring中使用-annotated方法 @Component。但是它们通常用于@Configuration的bean类。
对类进行@Configuration注解其主要目的是作为bean定义的来源。此外,@Configuration类允许通过调用@Bean同一类中的其他方法来定义bean间依赖关系。
(1) @Configuration注解类和@Bean注解方法
package com.etc.entity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BlogConfig {
@Bean(name="blog1")
public Blog getBlog1() {
return new Blog(1,"tom");
}
@Bean(name="blog2")
public Blog getBlog2() {
return new Blog(2,"jack");
}
}
(2)测试类
package com.etc.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.etc.entity.Blog;
import com.etc.entity.BlogConfig;
public class TestBlogBean {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(BlogConfig.class);
Blog blog1 = context.getBean("blog1",Blog.class);
System.out.println("blog1="+blog1.getId()+","+blog1.getTitle());
Blog blog2 = context.getBean("blog2",Blog.class);
System.out.println("blog2="+blog2.getId()+","+blog2.getTitle());
}
}
(3)实体类
package com.etc.entity;
public class Blog {
private int id;
private String title;
public Blog(int id, String title) {
super();
this.id = id;
this.title = title;
}
public Blog() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "Blog [id=" + id + ", title=" + title + "]";
}
}