day_01总结
下载: 课前资料
闪耀
day01总结
IDEA创建spring项目
文件 – >新建 -->项目
https://start.aliyun.com/
启动启动类
package com.jt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RunApplication {
public static void main(String[] args) {
SpringApplication.run(RunApplication.class, args);
}
}
启动成功
maven
jar包下载地址
maven 坐标的说明
<dependency>
<!--1.组ID: 公司域名倒写 www.tedu.cn-->
<groupId>junit</groupId>
<!--2.项目名称唯一-->
<artifactId>junit</artifactId>
<!--3.版本号 唯一的-->
<version>4.12</version>
<!--了解: 什么时候有效-->
<scope>test</scope>
</dependency>
day02总结
配置类 注解
@Configuration //将当前类标识为配置类
@ComponentScan(“com.jt”) //指定扫描的包路径, 可以扫描它的子孙包,用在配置类中
Spring创建对象—工厂模式
应用
问题: 任意对象都可以通过new的关键字 实例化吗?
答案: 当然不是, 抽象类对象 不可以直接实例化.
创建工厂
package com.jt.factory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
import java.util.Calendar;
/**
* FactoryBean是Spring提供的接口,spring自动完成调用.获取指定的对象
* 难点讲解:
* 1.@Component 标识类 则将该类交给Spring容器管理.
* 2.Spring中FactoryBean的讲解
* 如果spring加载的时候遇到FactoryBean接口
* 则会自动的执行重写的方法getObject/getObjectType
* 3.工厂模式说明:
* Map<Key:calendar,value=Calendar对象></>
*
* 核心功能:
* 1. key: 就是当前类型(如果自己编辑注解以注解为准)
* 2. value: 调用getObject获取的返回值对象
* 将上述的数据,交给Spring容器管理
* 该功能什么时候使用:
* 1. 某些对象不能直接实例化的.
* 2. 整合其它第三方框架对象时 经常使用.
*/
@Component("calendar")
public class CalendarFactory implements FactoryBean<Calendar> {
public CalendarFactory(){
System.out.println("工厂模式的无参构造");
}
//现阶段 大家理解为主. 未来写结构设计的时候,作用很大!!!!!
//动态执行该方法,获取返回值对象
@Override
public Calendar getObject() throws Exception {
//利用calendar的工具API 实现对象的创建
return Calendar.getInstance();
}
@Override
public Class<?> getObjectType() {
//固定写法. 一般直接xxx.class即可
return Calendar.class;
}
}
常用注解
@Configuration 标识配置类
@Bean 将自己方法的返回值交给Spring容器管理
@Component 将该类交给spring容器管理. 通过反射自动实例化对象
@ComponentScan(“com.jt”) 包扫描的注解 使Spring注解有效
day03
单例和多例
注解
@Scope //控制对象单例/多例
用于修饰spring管理的类里面的方法
@Scope("singleton") //默认值等价:@Scope() 单例模式
@Scope("prototype") // 多例模式
package com.jt.config;
import com.jt.demo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration //标识这是配置类
@ComponentScan("com.jt")
public class SpringConfig {
@Bean
@Scope("singleton") //默认值 单例模式
//@Scope("prototype") // 多例模式
public User user(){
return new User();
}
}
懒加载机制
说明: 如果Spring容器创建,对象立即创建则该加载方式为 “立即加载”, 容器启动创建
如果Spring容器创建,对象在被使用的时候才创建, 则称为"懒加载" 用时才创建
注解:
@Lazy 添加表示改为懒加载
测试说明: 主要测试对象中的无参构造什么时候执行!!!
用于修饰spring管理的类里面的方法
package com.jt.config;
import com.jt.demo.User;
import org.springframework.context.annotation.*;
@Configuration //标识这是配置类
@ComponentScan("com.jt")
public class SpringConfig {
@Bean
//@Scope("singleton") //默认值 单例模式
//@Scope("prototype") // 多例模式
@Lazy //懒加载
public User user(){
return new User();
}
}
关于lazy 使用场景的说明
场景1: 服务器启动时,如果加载太多的资源,则必然导致服务器启动慢, 适当的将不重要的资源设置为懒加载.
场景2: 有时用户会需要一些特殊的"链接",而这些链接的创建需要很长的时间.可以使用懒加载.
多例与懒加载的关系
说明: 只要对象是多例模式,则都是懒加载!, 在单例模式中控制懒加载才有效.
规则说明:
lazy true lazy false
单例模式: 有效 懒加载 有效 立即加载
多例模式: 无效 懒加载 无效 懒加载
对象生命周期
注解
@PostConstruct //在对象创建之后立即调用
@PreDestroy //对象消亡时 进行调用
案例
package com.jt.demo;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component //将对象交给Spring容器管理 key=person value:反射对象
public class Person {
public Person(){ //无参构造
System.out.println("张三出生了,资质拉满");
}
@PostConstruct //在对象创建之后立即调用
public void init(){
System.out.println("张三称为少年奇才");
}
//业务方法
public void doWork(){
System.out.println("迎娶美人鱼!!!");
}
@PreDestroy //对象消亡时 进行调用
public void destory(){
System.out.println("销毁:全世界哀悼");
}
}
执行结果:
分析:
张三出生了,资质拉满
张三称为少年奇才
在项目启动时,spring容器就会自动创建Person对象
,意味着只有项目启动成功他俩只是要被执行一次,可以用于初始化
张三出生了,资质拉满
张三称为少年奇才
在执行
AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext(SpringConfig.class);
迎娶美人鱼!!!
person.doWork();
销毁:全世界哀悼
//将容器关闭
context.close();
销毁:全世界哀悼
结束项目
依赖注入
注解
@Autowired注解
接口多实现的情况说明
@Autowired
@Qualifier("cat") //该注解不能单独使用,必须配合Autowired使用,根据key进行注入
//@Resource(name="cat")//等价于@Autowired+@Qualifier("cat")
@Value注解说明
调用properties文件里面的值
设置字节符
properties文件内容
#1.注意事项: key=value 等号连接 2.中间不要添加多余的空格
#2.说明: windows系统中有环境变量username=系统用户名称,以后写业务数据时,最好绕开关键字username
#3.编码规则: 程序默认读取properties文件时,采用ISO-8859-1编码
user.username=葫芦娃
应用
package com.jt.mapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Repository;
@Repository //为了让程序员开发有层级的概念
@PropertySource(value="classpath:/user.properties",encoding = "UTF-8")
//classpath:/ 代表resources的根目录
public class UserMapperImpl implements UserMapper{
//耦合性!!!!
// 表达式: 固定写法 springel表达式 取值方式 缩写spel表达式
// 规则:通过key动态获取spring容器中的value
@Value("${user.username}")
private String username;
@Override
public void addUser() {
System.out.println("新增用户:"+username);
}
}
Day04
业务层如何控制事务
事务: 可以保证数据的/原子性/一致性/持久性/隔离性.
说明: 业务层操作时,需要考虑数据库的事务.代码结构如下:
package com.jt.service;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
try {
System.out.println("开启数据库事务");
System.out.println("新增用户");
int a = 1/0;
System.out.println("提交数据库事务");
}catch (Exception e){
System.out.println("事务回滚");
}
}
}
问题说明
- 如果有多个方法,则每个方法都需要控制事务. 代码重复率高.
- 业务层service,应该只处理业务,不要和事务代码耦合在一起.否则扩展性不好,耦合性高.
如何解决: 采用代理机制解决
代理机制
1.1 代理模式特点
说明: 一般采用代理模式,主要的目的就是为了解耦.将公共的通用的方法(功能/业务)放到代理对象中. 由业务层专注于业务执行即可
代理特点
为什么使用代理? 因为自己不方便(没有资源)
代理的作用? 代理要解决(扩展)某些实际问题.
用户最终执行目标方法!!!.
1.2 动态代理-JDK模式
JDK代理的说明
JDK代理模式是java原生提供的API,无需导包
JDK代理要求: 被代理者必须 要么是接口,要么实现接口
灵活: 代理对象应该看起来和被代理者 一模一样!!! (方法相同)
被代理对象
package com.hyang.jt.server.Impl;
import com.hyang.jt.server.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
try {
System.out.println("开启数据库事务");
System.out.println("新增用户");
int a = 1/0;
System.out.println("提交数据库事务");
}catch (Exception e){
System.out.println("事务回滚");
}
}
}
编辑代理类
package com.jt.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy {
//传入target目标对象获取"代理对象"
//利用代理对象 实现方法的扩展
/**
* 1. 实现事务
*/
public static Object getProxy(Object target){
//1.获取类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
//2.获取接口数组类型
Class[] interfaces = target.getClass().getInterfaces();
//3.代理对象执行方法时的回调方法(代理对象调用方法时,执行InvocationHandler)
return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler01(target));
}
//要求必须传递目标对象
public static InvocationHandler invocationHandler01(Object target){
return new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk代理:事务开启");
//获取目标方法的返回值
Object result = method.invoke(target,args);
System.out.println("jdk代理:事务提交");
return result;
}
};
}
/**
* 2. addUser()方法的运行时间? 要求采用动态代理的方式完成
*/
public static Object getTimePorxy(Object target){
ClassLoader classLoader = target.getClass().getClassLoader();
Class[] interfaces = target.getClass().getInterfaces();
return Proxy.newProxyInstance(classLoader,interfaces,invocationHandlerTime(target));
}
//要求必须传递目标对象
public static InvocationHandler invocationHandlerTime(Object target){
return new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//开始时间
long startTime = System.currentTimeMillis();
//让目标方法执行 结果
Object result = method.invoke(target,args);
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime - startTime));
return result;
}
};
}
}
运用: 回调方法invocationHandler重命名和重写invoke方法,其他的照抄
编辑测试类
package com.jt;
import com.jt.config.SpringConfig;
import com.jt.proxy.JDKProxy;
import com.jt.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestSpring {
@Test
public void demo1(){
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
//获取目标对象
UserService userService = context.getBean(UserService.class);
//userService.addUser();
//获取代理对象
UserService proxy = (UserService) JDKProxy.getProxy(userService);
//代理对象执行被代理对象的方法, 调用invocationHandler01里面的invoke方法
proxy.addUser();
}
/**
* 计算addUser()方法的运行时间? 要求采用动态代理的方式完成
*/
@Test
public void demo2(){
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
//获取目标对象
UserService userService = context.getBean(UserService.class);
System.out.println(userService.getClass());
//userService.addUser();
//获取代理对象
UserService proxy = (UserService) JDKProxy.getTimePorxy(userService);
System.out.println(proxy.getClass());
//代理对象执行方法, 调用invoke方法
proxy.addUser();
}
}
demo1执行结果:
分析
由于被代理对象中int a = 1/0;错误,所以执行必报错,现在由jdk代理后项目没报错,并且没有提交数据库事务,而是直接结束该方法,实现类似事务回滚
1.3 动态代理-CGLIB代理
两种代理对比
jdk代理: 要求被代理者必须有/实现接口. 如果没有接口,则JDK代理不能正常执行.
cglib代理: 要求被代理者有无接口都可以. 代理对象是目标对象的子类, 重写子类方法
代理类
package com.jt.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGlibProxy {
public static Object getProxy(Object target){
//1.创建增强器对象
Enhancer enhancer = new Enhancer();
//2.设定父级 目标对象
enhancer.setSuperclass(target.getClass());
//3.定义回调方法 代理对象执行目标方法时调用
enhancer.setCallback(getMethodInterceptor(target));
//4.创建代理对象
return enhancer.create();
}
//需要传递target目标对象
public static MethodInterceptor getMethodInterceptor(Object target){
return new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("事务开始");
//执行目标方法
Object result = method.invoke(target,args);
System.out.println("事务提交");
return result;
}
};
}
}
运用: 回调方法重命名getMethodInterceptor和重写intercept方法,其他的照抄
CGLIB代理测试类
@Test
public void demo2(){
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
//获取目标对象
UserService userService = context.getBean(UserService.class);
UserService proxy = (UserService) CGlibProxy.getProxy(userService);
System.out.println(proxy.getClass());
proxy.addUser();
}
AOP
jar包
<!--引入AOPjar包文件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
AOP介绍
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
总结: Spring中的AOP 利用代理对象在不修改源代码的条件下,对方法进行扩展.
1. 开启AOP
@EnableAspectJAutoProxy //开启AOP
@Configuration
@ComponentScan("com.jt") //包扫描
@EnableAspectJAutoProxy //开启AOP
public class SpringConfig {
}
2. 切面类
切面 :
作用: 检索代理对象,扩展代理对象方法
实现: 切面 = 切入点表达式 + 通知方法
切入点表达式 : 检索代理对象
通知方法: 扩展代理对象方法
package com.jt.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component //将给类交给Spring容器管理 !!!
@Aspect //标识该类是一个切面
public class SpringAOP {
/**
* 知识回顾: AOP利用动态代理扩展目标方法.
* 公式: 切面 = 切入点表达式 + 通知方法
* 切入点表达式: 如果目标对象满足切入点表达式的判断(if),则spring自动为其创建代理对象
* 通知方法: 对目标方法进行扩展的封装方法.
* 目标对象的bean的ID: userServiceImpl
* 切入点表达式:
* 1. bean("bean的ID")
* AOP规则: 如果目标对象满足切入点表达式,则执行通知方法
*/
@Pointcut("bean(userServiceImpl)")
public void pointcut(){
}
//前置通知: 在目标方法执行之前执行.
@Before("pointcut()")
public void before(){
System.out.println("我是前置通知!!!!");
}
}
3.测试
package com.jt;
import com.jt.config.SpringConfig;
import com.jt.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestSpring {
@Test
public void demo1(){
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
//本来是目标对象,只不过与切入点表达式匹配,则动态生成代理对象.
UserService userService = context.getBean(UserService.class);
System.out.println(userService.getClass());
//由于是代理对象,所以方法可以扩展
userService.addUser();
}
}
常见通知类型
package com.jt.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component //将给类交给Spring容器管理 !!!
@Aspect //标识该类是一个切面
public class SpringAOP {
/**
* 知识回顾: AOP利用动态代理扩展目标方法.
* 公式: 切面 = 切入点表达式 + 通知方法
* 切入点表达式: 如果目标对象满足切入点表达式的判断(if),则spring自动为其创建代理对象
* 通知方法: 对目标方法进行扩展的封装方法.
* 目标对象的bean的ID: userServiceImpl
* 切入点表达式:
* 1. bean("bean的ID")
* AOP规则: 如果目标对象满足切入点表达式,则执行通知方法
*/
@Pointcut("bean(userServiceImpl)")
public void pointcut(){
}
//1.前置通知: 在目标方法执行之前执行.
@Before("pointcut()")
public void before(){
System.out.println("我是前置通知!!!!");
}
//2.后置通知: 在目标方法执行之后执行
@AfterReturning("pointcut()")
public void afterReturn(){
System.out.println("我是后置通知!!!!");
}
//3.异常通知: 目标方法执行报错时,执行该通知
@AfterThrowing("pointcut()")
public void afterThrowing(){
System.out.println("我是异常通知!!!!");
}
//4.最终通知: 目标方法之后都要执行的通知
@After("pointcut()")
public void after(){
System.out.println("最终通知都要执行");
}
//5.重点掌握 环绕通知: 在目标方法执行前后都要执行. 控制目标方法
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知执行前!!!!");
//底层调用动态代理的invoke方法,执行目标方法
Object result = joinPoint.proceed();
System.out.println("环绕通知执行后!!!!");
return result;
}
}