一、Spring的Context
1、基本概念
context
程序运行所需的整个环境。
Application Context
应用程序的上下文,用于整个应用程序的Bean实例化、配置和生命周期。
2、常用类型
读取XML的上下文:ClassPathXmlApplicationContext( )
读取注解的上下文:AnnotationConfigApplicationContext( )
二、控制反转(IOC)
1、基本概念
IOC(Inversion of Control)是一种设计思想,依赖注入(DI)是实现控制反转的一种方式。
面向对象编程,对象的创建、依赖有对象自己管理,具有较强的耦合性。通过将依赖关系交给第三方统一控制、装配、管理,从而实现解耦。
三、依赖注入(DI)
1、基本概念
依赖注入是一种解耦组件之间依赖关系的一种设计模式。将对象的依赖关系由对自己创建变为由外部容器创建和注入的过程。
2、注入形式
- setter方法注入
- 构造函数注入
- 接口注入
- 字段(Field)注入
setter方法注入
采用自动装配的方式
// CD
public interface CompactDisc {
void play();
}
// SgtPeppers CD
@Component
public class SgtPeppers implements CompactDisc{
@Override
public void play() {
System.out.println("Sgt. Pepper's Lonely Hearts Club Band is playing");
}
}
// 播放器
public interface MediaPlayer {
void play();
}
// CD播放器
@Component
public class CDPlayer implements MediaPlayer{
private CompactDisc cd;
// -----------------setter方法注入---------------------
// 注入的CompactDisc为接口,接口注入通常采用setter注入的方式完成
// --------------------接口注入-------------------------
@Autowired
public void setCd(CompactDisc cd) {
this.cd = cd;
}
@Override
public void play() {
cd.play();
}
}
// 配置类
@Configuration
@ComponentScan // 扫描当前包及其子包下的所有组件
public class CDPlayerConfig {
}
// main
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
CDPlayer cdPlayer = context.getBean(CDPlayer.class);
cdPlayer.play();
}
}
构造函数注入
采用自动装配方式
// CD播放器
@Component
public class CDPlayer implements MediaPlayer{
private CompactDisc cd;
// -----------------构造函数注入---------------------
@Autowired
public void CDPlayer(CompactDisc cd) {
this.cd = cd;
}
@Override
public void play() {
cd.play();
}
}
接口注入
采用自动装配的方式
// CD播放器
@Component
public class CDPlayer implements MediaPlayer{
private CompactDisc cd;
// -----------------setter方法注入---------------------
// 注入的CompactDisc为接口,接口注入通常采用setter注入的方式完成
// --------------------接口注入-------------------------
@Autowired
public void setCd(CompactDisc cd) {
this.cd = cd;
}
@Override
public void play() {
cd.play();
}
}
字段(Field)注入
@Component
public class CDPlayer implements MediaPlayer{
//------------------------字段注入-------------------------
@Autowired
private CompactDisc cd;
@Override
public void play() {
cd.play();
}
}
四、切面编程(AOP)
1、基本概念
AOP面向切面编程是一种编程思想,将贯穿于多个应用对象的逻辑抽象成单独的模块,减少代码的重复性。
2、注解定义切面
使用javaConfig来配置
// 1、定义表演接口
public interface Performance {
void perform();
}
// 2、戏曲为表演的实现类
public class Xiqu implements Performance{
@Override
public void perform() {
System.out.println("表演戏曲");
}
}
// 3、为表演配置切面
// ======================无切点===============================
@Aspect
public class Audience {
@Before("execution(**demo1.Performance.perform(..))")
public void silenceCellPhones() {
System.out.println("Silencing cell phones");
}
@Before("execution(**demo1.Performance.perform(..))")
public void takeSeats() {
System.out.println("Taking seats");
}
@After("execution(**demo1.Performance.perform(..))")
public void applause() {
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("execution(**concert.Performance.perform(..))")
public void demandRefund() {
System.out.println("Demanding a refund");
}
}
// ====================切点简化表达式============================
@Aspect
public class Audience {
@Pointcut("execution(* AOP.demo1.Performance.perform(..))")
public void performance(){}
@Before("performance()")
public void silenceCellPhones() {
System.out.println("Silencing cell phones");
}
@Before("performance()")
public void takeSeats() {
System.out.println("Taking seats");
}
@After("performance()")
public void applause() {
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("performance()")
public void demandRefund() {
System.out.println("Demanding a refund");
}
}
// =========================切面环绕通知============================
@Aspect
public class Audience {
@Pointcut("execution(* AOP.demo1.Performance.perform(..))")
public void performance(){}
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Silencing cell phones(Around)");
System.out.println("Taking seats(Around)");
jp.proceed();
// int result = 10/0; // 可以抛出异常
System.out.println("CLAP CLAP CLAP!!!(Around)");
} catch (Throwable e) {
System.out.println("Demanding a refund(Around)");
}
}
}
// 4、配置文件
@Configuration
@EnableAspectJAutoProxy // 开启AOP代理
public class AOPConfig {
@Bean
public Audience audience(){
return new Audience();
}
@Bean
public Xiqu xiqu(){
return new Xiqu();
}
}
// 5、运行测试
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AOPConfig.class);
Performance xiqu = (Performance) context.getBean("xiqu");
xiqu.perform();
}
}
// 6、输出
Silencing cell phones
Taking seats
表演戏曲
CLAP CLAP CLAP!!!
3、定义环绕通知切面
jp.proceed()调用后,执行perform()函数。不调用,只执行切面通知。
@Aspect
public class Audience {
@Pointcut("execution(* AOP.demo1.Performance.perform(..))")
public void performance(){}
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Silencing cell phones(Around)");
System.out.println("Taking seats(Around)");
jp.proceed();
// int result = 10/0; // 可以抛出异常
System.out.println("CLAP CLAP CLAP!!!(Around)");
} catch (Throwable e) {
System.out.println("Demanding a refund(Around)");
}
}
}
4、切点方法中含有参数
// 1、定义表演:含参数
public interface Performance {
void perform(int ticketPrice);
}
// 2、定义戏曲:含参数
public class Xiqu implements Performance {
@Override
public void perform(int ticketPrice) {
System.out.println("表演戏曲,票价:"+ ticketPrice + "元");
}
}
// 3、定义切面通知:处理参数
//==========================单个参数==================================
@Aspect
public class Audience {
//接收参数类型+指定参数名称
@Pointcut("execution(* AOP.demo2.Performance.perform(int))"+"&& args(ticketPrice)")
public void performance(int ticketPrice){}
@Before("performance(ticketPrice)")
public void silenceCellPhones(int ticketPrice) {
System.out.println("Silencing cell phones");
}
@Before("performance(ticketPrice)")
public void takeSeats(int ticketPrice) {
System.out.println("Taking seats");
}
@After("performance(ticketPrice)")
public void applause(int ticketPrice) {
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("performance(ticketPrice)")
public void demandRefund(int ticketPrice) {
System.out.println("Demanding a refund");
}
}
//==========================多个参数==================================
@Aspect
public class Audience {
@Pointcut("execution(* AOP.demo2.Performance.perform(int, float))"+"&& args(ticketPrice, startTime)")
public void performance(int ticketPrice, float startTime){}
@Before("performance(ticketPrice,startTime)")
public void silenceCellPhones(int ticketPrice, float startTime) {
System.out.println("Silencing cell phones");
}
@Before("performance(ticketPrice,startTime)")
public void takeSeats(int ticketPrice, float startTime) {
System.out.println("Taking seats");
}
@After("performance(ticketPrice,startTime)")
public void applause(int ticketPrice, float startTime) {
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("performance(ticketPrice,startTime)")
public void demandRefund(int ticketPrice, float startTime) {
System.out.println("Demanding a refund");
}
}
5、AOP案例(日志管理)
自定义注解
/**
* 日志注解(该注解目前用于练习切面AOP)
*/
@Target(ElementType.METHOD) // 方法上的注解
@Retention(RetentionPolicy.RUNTIME) // 运行时注解
@Documented // 生成文档
public @interface LogAnnotation {
/**
* 日志描述信息
*/
String value() default "";
}
定义日志切面
需要导入的包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
@Aspect // 标识为切面类
@Component // 注入IOC容器
/**
* 日志切面逻辑
*/
public class LogAspect {
@Pointcut("@annotation(com.itsyz.annotation.LogAnnotation)")
public void logPointCut() {
}
/**
* 前置通知 joinPoint为连接点:业务方法与切面方法的桥梁
* @param joinPoint
*/
@Before("logPointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("before");
}
/**
* 环绕通知
*/
@Around("logPointCut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
// 日志输出
String name = joinPoint.getSignature().getName();
System.out.println("调用了" + name + "方法");
joinPoint.proceed();
System.out.println("参数:" + joinPoint.getArgs());
}
/**
* Before与After主要为了查看一下执行顺序
*/
@After("logPointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("after");
}
}
开启AOP支持代理
@EnableAspectJAutoProxy //开启AOP支持
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@EnableWebMvc
@EnableAsync //开启异步任务支持(主要)
@EnableAspectJAutoProxy //开启AOP支持
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
具体业务逻辑添加日志
/**
* 发布事件
*/
@Autowired
ApplicationContext applicationContext;
@LogAnnotation(value = "发布事件")
@GetMapping("/publishEvent")
public void publishEvent() {
System.out.println(1);
// 打印当前线程的信息
System.out.println("执行当前线程的名称1: " + Thread.currentThread().getName());
Course courses = new Course(1, "Java", "Java.jpg", 100);
// 发布自定义的课程测试事件
applicationContext.publishEvent(new CoursesTestEvent(this, courses.getTitle(),courses.getThumb()));
System.out.println(2);
System.out.println("执行当前线程的名称2: " + Thread.currentThread().getName());
}
打印结果
调用了previewPdf方法
before
/D:/CodeSpace/ProjectDevelopment/target/classes/
after
参数:[Ljava.lang.Object;@68eef6b4