Spring
装配Bean
spring装配bean有三种方式:
- 在xml中显式装配
- 在java中显式装配
- 隐式的bean自动装配
自动装配的两个操作:
- 组件扫描:component scanning,spring通过扫描发现应用上下文创建的bean
- 自动装配:autowiring,spring自动满足bean之间的依赖
自动装配的两种方式:
- 使用xml配置
- 使用注解(推荐)
使用xml配置
设置autowire=“byName”,spring会根据上下文找到id与实体类setter方法去掉set后首字母小写的名称相同的bean进行装配,比如实体类中的setter方法为setCat1(),那么需要找到id为”cat1“的bean进行装配
<bean id="dog" class="com.sen.pojo.Dog"/>
<bean id="cat" class="com.sen.pojo.Cat"/>
<bean id="person" class="com.sen.pojo.Person" autowire="byName">
<property name="name" value="sen"/>
</bean>
autowire="byName"虽然是根据id名字进行自动装配,但是也会判断类型,假若类型对应不上,就不会装配,比如,将dog和cat的id名称互换后,自动装配失败。
<bean id="cat" class="com.sen.pojo.Dog"/>
<bean id="dog" class="com.sen.pojo.Cat"/>
<bean id="person" class="com.sen.pojo.Person" autowire="byName">
<property name="name" value="sen"/>
</bean>
另外一种是设置autowire=“byType”,spring将与字段类型匹配的bean进行自动装配,通过byType进行自动装配不要求id名与字段名一致,但是要求这个bean类型在当前上下文是唯一的,
<bean id="cat123" class="com.sen.pojo.Dog"/>
<bean id="dog1" class="com.sen.pojo.Cat"/>
<!--自动装配的bean不允许当前上下文出现类型相同的第二个bean-->
<!--<bean id="dog2" class="com.sen.pojo.Cat"/>-->
<bean id="person" class="com.sen.pojo.Person" autowire="byType">
<property name="name" value="sen"/>
</bean>
使用注解@Autowired自动装配
使用注解要先做两步工作:
1.使用注解前,要在spring的xml配置文件头引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
2.开启注解支持
<context:annotation-config/>
使用@Autowired:
同样先在spring容器中注册bean
<context:annotation-config/>
<bean id="dog" class="com.sen.pojo1.Dog"/>
<bean id="cat" class="com.sen.pojo1.Cat"/>
<bean id="person" class="com.sen.pojo1.Person">
<property name="name" value="chen"/>
</bean>
然后在实体类中,对需要自动装配的字段添加注解@Autowired,使用注解自动装配不再需要字段的setter方法
public class Person {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
}
可以设置注解@AutoWired的值required = false,(默认为ture),标记这个字段可以null
public class Person {
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
}
如果没有设置为false并且没有找到对应的bean装配,则会抛出异常UnsatisfiedDependencyException
注意:
- @AutoWired优先按类型自动装配的,假若有多个同类型的bean,再按照id进行匹配,如果id匹配不上,则报错
- 需要指定id名装配指定的bean,可以搭配@Qualifier使用
使用@Qualifier:
使用@Qualifier可以指定id名装配指定的bean
public class Person {
@Autowired
private Cat cat;
@Autowired
@Qualifier(value = "dog1") //指定装配id为"dog1"的bean
private Dog dog;
private String name;
}
使用注解开发
@Component
之前使用在xml中使用bean标签进行bean注入,但是实际中大多是通过使用注解进行注入,使用@Component注解标记一个类为spring容器的bean,参数为设置这个bean的id。不设置参数默认为这个类名的首字母小写的名称,如Cat的默认id为cat
Cat类:
package com.sen.pojo;
import org.springframework.stereotype.Component;
@Component
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
Dog类:
package com.sen.pojo;
import org.springframework.stereotype.Component;
@Component
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
在java类中使用@Component注册好bean后,需要在xml中扫描指定的包,才能获取这些bean
<context:component-scan base-package="com.sen.pojo"/>
<bean id="person" class="com.sen.pojo.Person" autowire="byName">
<property name="name" value="sen"/>
</bean>
使用@Component将Person也注册为bean,并使用@Autowired自动装配,彻底告别在xml中配置bean。
Person类:
@Component("person")
public class Person {
@Autowired
Cat cat;
@Autowired
Dog dog;
@Value("sen")
private String name;
}
解析:
- 使用
@Component("cat")
类似于<bean id="cat" class="当前注解的类Cat"/>
- 使用
@Value("sen")
类似于bean中的属性配置<property name="name" value="sen"/>
最后测试代码如下:
import com.sen.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void TestAnnotation(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = context.getBean("person", Person.class);
person.getDog().shout();
person.getCat().shout();
}
}
/*
输出如下:
wang~
miao~
*/
三个衍生注解
为了区分MVC三层架构,Spring提供三种不同的注解去标记这些层,但是本质上都是@Component,功能都是一样的
- @Service:service层
- @Controller:web层
- @Repository:dao层
使用@scope设置作用域
使用@scope标记bean的作用域,提供了singleton、prototype参数
- singleton:默认为单例模式,每次请求bean都是返回同一个对象
- prototype:设置为原型模式,每次请求bean都会创建一个新的对象
总结
- XML适用于任何场景,结构清晰,维护方便
- 注解不是自己提供的类使用不了,开发简单方便
- 推荐使用xml与注解结合开发
- xml管理Bean
- 注解完成属性注入
使用Java类进行配置
之前是在一个xml文件中配置bean,现在使用纯Java配置,通过在一个类上添加注解@Configuration标识该类为一个配置类,然后在这个类中对方法添加@Bean注解注册bean,如下:
package com.sen.pojo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 标识这个类是配置类
public class MyConfig {
@Bean //注册一个bean,方法名就是bean的id
public Dog happy(){
return new Dog();
}
@Bean
public Cat cat(){
return new Cat();
}
}
注意,实体类Dog和Cat要注册到Spring容器中成为bean
@Component("dog")
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
@Component("cat")
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
测试:
@Test
public void TestJavaConfig(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = context.getBean("happy", Dog.class); // 这里使用的bean id是上面定义的方法名
Cat cat = context.getBean("cat", Cat.class);
dog.shout();
cat.shout();
}
如果需要导入其他的java配置类,可以使用@Import()注解,如下:
再编写一个配置类
package com.sen.pojo;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OtherConfig {
}
在原配置类中导入
@Configuration
@Import(OtherConfig.class) // 参数为其他配置类的Class对象
public class MyConfig {
@Bean
public Dog happy(){
return new Dog();
}
@Bean
public Cat cat(){
return new Cat();
}
}
AOP-面向切面编程
AOP(Aspect Oriented Programming):面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Spring的AOP就是将公共的业务(日志、安全等)和领域业务结合起来,当执行领域业务时,将会把公共业务加进来,实现公共业务加进来,实现公共业务的重复利用,领域业务更纯粹,程序员专注领域业务。
AOP本质就是动态代理。
AOP中的一些名词:
-
横切关注点:跨越应用程序多个模块的方法或功能,与业务逻辑无关,比如日志、安全、缓存等
-
切面(Aspect):横切关注点被模块化的特殊对象,即一个类。
-
通知(Advice):切面必须要完成的工作,即类中的方法
-
目标(Target):被通知的对象
-
代理(Proxy):向目标对象应用通知之后创建的代理角色
-
切入点(PointCut):切面通知执行的”地点“
-
连接点(JointPoint):与切入点匹配的执行点
流程大致如下:
-
确定横切关注点,即关注添加哪些功能
-
将其模块化为一个切面
-
在这个切面了实现通知功能
-
然后在目标中找到切入点
-
植入连接点
实现AOP
首先在maven中导入AOP依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
第一种方式——使用spring API实现
1 首先编写业务接口和业务类
package com.sen;
public interface UserService {
void add();
void delete();
void query();
void update();
}
package com.sen;
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("add a user");
}
public void delete() {
System.out.println("delete a user");
}
public void query() {
System.out.println("query a user");
}
public void update() {
System.out.println("update a user");
}
}
2 编写增强类,一个前置增强,一个后置增强
package com.sen;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
// 前置增强类,实现MethodBeforeAdvice接口
public class Log implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "'s " + method.getName() + " has been executed");
}
}
package com.sen;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
// 后置增强类,实现AfterReturningAdvice接口
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("finish" + target.getClass().getCanonicalName() + "'s " + method.getName());
}
}
3 注册bean,此处使用xml方式,配置AOP,确定切入点,执行环绕,beans.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!--需要添加aop的头文件信息--->
<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.xsd">
<!--注册bean-->
<bean id="userService" class="com.sen.UserServiceImpl"/>
<bean id="log" class="com.sen.Log"/>
<bean id="afterLog" class="com.sen.AfterLog"/>
<!--配置AOP-->
<aop:config>
<!--确定切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.sen.UserServiceImpl.*(..))"/>
<!--执行环绕-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
4 测试
import com.sen.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
userService.delete();
}
}
第二种方式——使用自定义类
1 写一个切面类
package com.sen;
public class MyPointCut {
public void before(){
System.out.println("-----方法执行前-----");
}
public void after(){
System.out.println("-----方法执行后-----");
}
}
2 在xml中注册bean
<bean id="userService" class="com.sen.UserServiceImpl"/>
<bean id="myPointCut" class="com.sen.MyPointCut"/>
3 配置AOP
<aop:config>
<!--引入切面(上面注册的bean)-->
<aop:aspect ref="myPointCut">
<!--确定切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.sen.UserServiceImpl.*(..))"/>
<!--设置前后增强-->
<aop:before pointcut-ref="pointcut" method="before" />
<aop:after pointcut-ref="pointcut" method="after"/>
</aop:aspect>
</aop:config>
4 测试
import com.sen.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
userService.delete();
}
}
第三种方式——使用注解
1 编写一个切面类,并用注解标记
package com.sen;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect //标记这个类是一个切面
public class AnnotationPointCut {
// 设置切入点的前置增强
@Before("execution(* com.sen.UserServiceImpl.*(..))")
public void before(){
System.out.println("-----方法执行前-----");
}
// 设置切入点的后置增强
@After("execution(* com.sen.UserServiceImpl.*(..))")
public void after(){
System.out.println("-----方法执行后-----");
}
}
2 在xml中注册bean,并开启aop注解支持
<bean id="userService" class="com.sen.UserServiceImpl"/>
<bean id="annotationPointCut" class="com.sen.AnnotationPointCut"></bean>
<!--开启aop注解支持-->
<aop:aspectj-autoproxy/>
3 测试
import com.sen.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
userService.delete();
}
}