1.1 Spring概述
1.1.1 Spring的简史
- 第一阶段:xml配置
- 第二阶段:注解配置(应用的基本配置如数据库配置用xml,业务配置用注解)
- 第三阶段:java配置(Spring4.x和Spring Boot都推荐Java配置)
1.1.2 Spring概述
Spring框架是一个轻量级的企业级开发一站式解决方案.基于Spring解决Java EE开发遇到的所有问题.Spring框架主要提供了IOC容器、AOP、数据访问、Web开发、消息、测试等相关技术支持.
Spring使用简单的POJO(即无任何限制的普通Java对象)来进行企业级开发.每一个被Spring管理的Java对象被称之为Bean,而Spring提供了一个IOC容器来初始化对象,解决对象间的依赖管理和对象使用
Spring是模块化的,你可以只使用你需要的Spring模块
1.2 Spring项目快速搭建
1.3 Spring基础配置
Spring框架本身有四大原则(Spring所有功能的设计和实现都基于此四大原则的)
- 使用POJO进行轻量级和最小侵入式开发
- 通过依赖注入和基于接口编程实现松耦合
- 通过AOP和默认习惯进行声明式编程
- 使用AOP和模板减少模式化代码
1.3.1 依赖注入
我们经常说的依赖注入和反转控制在Spring环境下是等同的概念,控制反转是通过依赖注入实现的.所谓依赖注入指的是容器负责创建对象和维护对象之间的关系,而不是通过对象本身负责自己的创建和解决自己的依赖.
依赖注入的主要目的是为了解耦,体现了一种组合的理念.
Spring IOC容器(ApplicationContext)负责创建bean,并通过容器将功能类Bean注入到你需要的Bean中.Spring提供使用xml注解、Java配置、groovy配置实现Bean的创建和注入
无论xml配置、注解配置还是Java配置,都被称为配置元数据,所谓元数据即描述数据的数据.元数据本身不具备任何可执行的能力,只能通过外界代码来对元数据解析后进行一些有意义的操作.Spring容器解析这些配置元数据进行Bean初始化、配置和管理依赖.
声明Bean的注解:
- @Componet组件,没有明确的角色
- @Service 在业务逻辑层(service层)使用
- @Repository在数据访问层(dao层)使用
@Controller在展现层(MVC-Spring MVC)使用
注入Bean的注解,一般情况下通用
@Autowired:spring提供的注解
- @Inject:JSR-330提供的注解
@Resource:JSR-250提供注解
@Autowired 、@Inject、@Resource 可注解在set方法上或者属性上,
@Autowired 、@Inject、@Resource 使用这些注解是将实体Bean注入到当前使用类中,让当前使用类具有注入类一样的功能
@Componet、@Service 、@Repository、@Controller注解声明当前类是Spring管理的一个Bean,它们是等效的,可根据需求选择
@Configuration声明当前类是一个配置类
@ComponentScan,自动扫描包下所有使用@Service、@Component、@Repository和@Controller的类,并注册为Bean
1.3.2 Java配置
Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置;Java配置也是Spring Boot推荐的配置方式.
Java配置是通过@Configuration和@Bean来实现的
@Configuration 声明当前类是一个配置类,相当于一个Spring配置的xml文件
@Bean注解在方法上,声明当前方法的返回值是一个Bean,Bean的名称是方法名
何时使用Java配置或者注解配置呢?
原则:全局配置使用Java配置(如数据库相关配置、MVC相关配置),业务Bean的配置使用注解配置(@Service、@Componet、@Repository、@Controller)
- 注入FunctionService的bean时候直接调用functionService(),另一种注入的方式是直接当参数.在Spring容器中,只要容器中存在某个Bean,就可以在另一个Bean的声明方法的参数中写入.如图所示
1.3.3 AOP
AOP:面向切面编程,相当于OOP面向对象编程
Spring的AOP的存在目的是为了解耦.AOP可以让一组类共享相同的行为.在OOP中只能通过继承类和实现接口,来使代码的耦合度增强,且继承只能单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足.
Spring支持AspectJ的注解式切面编程.
使用@Aspect声明是一个切面.
使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数
其中@After、@Before、@Around参数的拦截规则为切点(PointCut),为了使切点复用,可使用@PointCut专门定义拦截规则,然后在@After、@Before、@Around的参数中调用
其中符合条件的每一个被拦截处为连接点(JoinPoint)
基于注解拦截和基于方法规则拦截两种方式.其中基于注解式拦截能够很好地控制要拦截的粒度和获得丰富的信息,Spring本身在事务处理(@Transcation)和数据缓存(@Cacheable等)上面都使用此种形式的拦截
示例
(1) :添加spring aop支持及AspectJ依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.6RELEASE</version>
</dependency>
(2) : 编写拦截规则的注解
1、元注解
元注解是指注解的注解。包括 @Retention @Target @Document @Inherited四种。
1.1、@Retention: 定义注解的保留策略
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
1.2、@Target:定义注解的作用目标
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
1.3、@Document:说明该注解将被包含在javadoc中
1.4、@Inherited:说明子类可以继承父类中的该注解
package com.example.demo.packageAop;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
String name();
}
(3) : 编写使用注解的被拦截的类
package com.example.demo.packageAop;
import org.springframework.stereotype.Service;
@Service
public class DemoAnnotationService {
@Action(name="注解式拦截的add操作")
public void add(){}
}
(4) : 编写使用方法规则被拦截的类
package com.example.demo.packageAop;
import org.springframework.stereotype.Service;
@Service
public class DemoMethodService {
public void add(){}
}
(5): 编写切面
package com.example.demo.packageAop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect//1
@Component//2
public class LogAspect {
@Pointcut ("@annotation(com.example.demo.packageAop.Action)")//3
public void annotationPointCut(){}
@After("annotationPointCut()")//4
public void after(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Action annotation = method.getAnnotation(Action.class);
System.out.println("拦截式注解"+annotation.name());//5
}
@Before("execution(* com.example.demo.packageAop.DemoMethodService.*(..))")//6
public void before(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法拦截规则"+method.getName());
}
}
代码解释
①:通过@Aspect 注解声明一个切面
②:通过@Component 让此切面成为Spring容器管理的Bean
③:通过@PointCut注解声明切点
④:通过@After注解声明一个建言,并使用@PointCut定义的切点
⑤:通过反射可获得注解上的属性,然后做日志记录相关的操作,下面的相同
⑥:通过@Before注解声明一个建言,此建言直接使用拦截规则作为参数
(6) : 配置类
package com.example.demo.packageAop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan
@EnableAspectJAutoProxy//使用注解开启Spring对AspectJ代理的支持
public class AopConfig {
}
(7): 运行
package com.example.demo.packageAop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
DemoAnnotationService demoAnnotationService = context.getBean(DemoAnnotationService.class);
DemoMethodService demoMethodService = context.getBean(DemoMethodService.class);
demoAnnotationService.add();
demoMethodService.add();
context.close();
}
}