注: Ioc是Spring全家桶各个功能模块的基础,创建对象的容器,AOP是以IoC为基础,AOP是切面编程,抽象化的面向对象。
一、Spring IoC
控制反转,将对象的创建进行反转,常规情况下,对象需要开发者手动创建,使用IoC开发者不需要手动创建对象,而是由IoC容器根据需求自动创建项目所需要的对象。 不用IoC:所有对象以及值需要开发者自己赋值创建 使用IoC:对象不用开发者创建,交给Spring框架完成
- 基于xml
-
开发者把需要的对象在XML中进行配置,Spring框架会读取这个配置,根据配置信息来创建对象。
-
@Data
public class TestConfig {
private Integer id;
private String name;
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
">
<bean class="com.test.config.TestConfig" id="testConfig">
<property name="id" value="1" />
<property name="name" value="测试" />
</bean>
</beans>
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
System.out.println( context.getBean("testConfig"));
//输出结果是 TestConfig(id=1, name=测试)
ClassPathXmlApplicationContext 会解析xml文件,并把bean里面property标签的参数,通过Set赋值进对象里。 通过xml配置bean参数,再通过ApplicationContext.getBean('bean名”) 来获取自动生成好的对象
- 基于注解
- 配置类
- 用一个java类来代替XML文件,把XML的配置内容放到配置类中。
- 配置类
@Configuration
public class BeanConfiguration {
//默认方法名,也可以指定名
// @Bean(value = "config")
@Bean
public TestConfig testConfig(){
TestConfig testConfig = new TestConfig();
testConfig.setId(1);
testConfig.setName("测试");
return testConfig;
}
}
ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfiguration.class);
System.out.println(context.getBean("testConfig"));
// 输出结果 TestConfig(id=1, name=测试)
//通过类也可以去获取
System.out.println(context.getBean(TestConfig.class));
// 输出结果 TestConfig(id=1, name=测试)
这是通过声明配置类的方式,来读取bean对象。在通过ApplicationContext来获取Bean对象,默认方法名获取,也可以加@Bean(value="config")去指定名。@Bean是声明Bean对象,contenxt可以根据方法名获取,也可以通过类获取 【配置类很多的时候可以指定包名】
//一个包下有若干个配置类,那么通过填写包名去获取bean对象
ApplicationContext context = new AnnotationConfigApplicationContext("com.test.config");
System.out.println(context.getBean("config"));
System.out.println(context.getBean(TestConfig.class));
//输出结果 TestConfig(id=1, name=测试)
- 扫包-注解 不在依赖于XML或者配置类,而是直接将Bean的创建交给目标类,在目标类添加注解来创建
// 加上component ,spring框架读到这个类的时候,会将这个注释的类 直接创建放入到IoC里面
@Component
public class TestConfig {
@Value("1")
private Integer id;
@Value("测试")
private String name;
}
ApplicationContext context = new AnnotationConfigApplicationContext("com.test.config");
System.out.println(context.getBean("config"));
System.out.println(context.getBean(TestConfig.class));
//输出结果 TestConfig(id=null, name=null)
加上component ,spring框架包扫描的时候读到这个类有@Component注释的时候,会将这个类直接创建放入到IoC容器里面,@Value可以为变量赋值。 自动创建对象,依赖注入(A配置类里面有B配置类,将B配置类也注入到A配置类里面)
@Data
@Component
public class GlobalConfig {
@Value("1")
private Integer id;
@Value("全局测试")
private String name;
// 两个组合可以通过名字去找
// @Autowired
// @Qualifier("config")
@Autowired
private TestConfig testConfig;
}
ApplicationContext context = new AnnotationConfigApplicationContext("com.test.config");
System.out.println(context.getBean(GlobalConfig.class));
//输出结果 GlobalConfig(id=1, name=全局测试, testConfig=TestConfig(id=1, name=测试))
- 依赖注入
- @Autowired(根据bean类型进行注入)
- @Resource(先根据name去获取name为空就通过bean类型去取)
- @Qualifier(@Autowired配合@Qualifier 可以根据名字映射)
二、AOP
概念:面向切面编程,是一种抽象化的面向对象编程,对面向对象编程的一种补充(要在多个指定位置统一处理相同业务逻辑,则可以用到切面编程)。底层使用动态代理机制 作用:打印日志、事务、权限处理,【做到核心代码和非业务代码的解耦合】
- execution 用法【更侧重签名方法签名的匹配】
- 表达式语法结构
- 返回值类型模式
execution( void com.xx.jj.service.userService.add(...) )【指返回void类型的com.xx.jj包下userService下的add的方法名】 - 类名模式
execution( void com.xx.jj..*)【包名后跟..表示当前包和子包 * 表示包下的所有类】 - 方法名模式
execution(* *.add*(..))【表示所有add前缀的方法】 - 参数模式(..)
execution( * *.add*(int,..))【表示所有add前缀且第一个参数为int,后面跟任意数量参数的方法名】 - 主要格式 【execution([修饰符模式] 返回值类型模式 [类名模式]方法名模式(参数模式)[异常模式])】 ..任意个数 *模糊匹配 void指定类型 com.xx.jj 具体包名 com.xx.jj.service.userService 具体类名
- 常用表达式
execution(* com.example.dao..*.*(..))【com.example.dao下的所有类的所有方法】execution(* com.example.service.UserService.*(..))【com.example.service.UserService.*(..) userService指定类下的所有方法】execution(* com.example.dao.GenericDAO+.*(..))【匹配com.example.dao.GenericDAO下的所有实现类的方法】execution(* save*(..))【匹配save开头的所有方法名】
- 返回值类型模式
- 多表达式组合
- 或(|| 或 or )
execution(* *.add(..)) || execution(* *.delete(..))【表示所有add或者delete方法】 - 与(&& 或 and )
execution(* *..*.*(..)) && args(String)【表示所有包下的所有类下的所有方法名 ,且参数包含String类型的方法】 - 非(! 或 not )
!execution(* *.get*(..))【表示除了以get为前缀的方法名】
- 或(|| 或 or )
- 表达式语法结构
- within用法【更侧重包的范围匹配】
- 用法
- within(包名..*) 指定包下所有类
- within(com.example.service.UserService) 匹配指定类
- 用法
-
@annotation表达式
//作用在方法上
@Target(ElementType.METHOD)
//注释的注释 决定@Target保留方式 source保留在源文件,编译后失效 class文件jvm加载后遗弃, runtime保留到jvm加载class之后
@Retention(RetentionPolicy.RUNTIME)
public @interface LoggerTest {
}
@Pointcut("@annotation(com.test.annotation.LoggerTest)")
public void loggableMethods() {
System.out.println("进入了");
}
@Before("loggableMethods()")
public void before(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"方法的参数是"+ Arrays.toString(joinPoint.getArgs()));
}
可以给 aop将注释作为切入点,将注释了@LoggerTest的方法进行切入 。
@LoggerTest
@Override
public int testLog(int num1, int num2) {
return 0;
}
- 打印日志 1.创建切面类
@Component
@Aspect
public class LoggerAspect {
@Before("execution(public int com.test.calTest.CalculateImpl.*(..))")
public void before(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"方法的参数是"+joinPoint.getArgs().toString());
}
@AfterReturning(value="execution(public int com.test.calTest.CalculateImpl.*(..))",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"方法的结果是"+result);
}
}
@Component
public class CalculateImpl implements Calculate {
@Override
public int add(int num1, int num2) {
return num1 + num2;
}
@Override
public int sub(int num1, int num2) {
return num1 - num2;
}
@Override
public int mul(int num1, int num2) {
return num1 * num2;
}
@Override
public int div(int num1, int num2) {
return num1 / num2;
}
}
<?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"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- <bean class="com.test.config.TestConfig" id="testConfig">-->
<!-- <property name="id" value="1" />-->
<!-- <property name="name" value="测试" />-->
<!-- </bean>-->
<!-- 自动扫包-->
<context:component-scan base-package="com.test.calTest" ></context:component-scan>
<!-- 也可以在aspect类上加个 @EnableAspectJAutoProxy 开启自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
<!-- 或者给 aspect加一个-->
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Calculate calculate = context.getBean(Calculate.class);
System.out.println(calculate.add(1,2));
System.out.println(calculate.sub(1,2));
System.out.println(calculate.mul(1,2));
System.out.println(calculate.div(1,2));
/**输出结果
add方法的参数是[1, 2]
add方法的结果是3
3
sub方法的参数是[1, 2]
sub方法的结果是-1
-1
mul方法的参数是[1, 2]
mul方法的结果是2
2
div方法的参数是[1, 2]
div方法的结果是0
0
**/
- @PointCut 切入点,主要是提高个代码的复用性,多个@Before可以直接通过 切入点方法名()的方式去切入。让代码更整洁。
- 五种通知方式
- 正常顺序【@Aroundproceed之前->@Before->执行方法->@AfterReturning->@After->@Aroundproceed之后】
- 异常顺序【@Aroundproceed之前->@Before->执行方法->@AfterThrowing->@After->throw e】
@Pointcut("@annotation(com.test.annotation.LoggerTest)")
public void loggableMethods() {
System.out.println("进入了");
}
//环绕通知
@Around("loggableMethods()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("proceed之前打印");
Object result = joinPoint.proceed();
System.out.println("之后打印"+result.toString());
return result;
}
//前业务
@Before("loggableMethods()")
public void before(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"方法的参数是"+ Arrays.toString(joinPoint.getArgs()));
}
//后业务
@After("loggableMethods()")
public void after(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"执行方法之后");
}
//异常
@AfterThrowing(value = "loggableMethods()",throwing = "e")
public void afterThrow(JoinPoint joinPoint,Exception e) {
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"报错的结果"+e.getMessage());
}
//后返回值
@AfterReturning(value="loggableMethods()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"方法的结果是"+result);
}
/**输出结果
proceed之前打印
testLog方法的参数是[1, 2]
testLog方法的结果是0
testLog执行方法之后
之后打印0
**/
/**
异常处理的结果
proceed之前打印
div方法的参数是[1, 0]
div报错的结果/ by zero
div执行方法之后
这里就是报错了
**/