目录
使用纯Java方式配置Spring(类似Spring Boot)
Spring介绍
Spring核心是Ioc和Aop
Ioc就是把对象放入容器中(xml配置文件,使用bean标签,注解),从容器中获取使用,都是单例对象比如dao类 service类 controller类 工具类
不需要放入容器中:实体类,servlet和listener(过滤器)和filter(监听器)
spring是一个容器,管理对象,给属性赋值,底层是创建对象,使用的是反射机制
ioc和aop:减少对代码的改动,也能实现不同的功能,实现解耦合
pom.xml配置文件中加入依赖 idea才可以通过new 创建Spring
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
set注入
属性赋值,引用类型赋值
默认无参构造
class是实体类的路径从 com. 开始
id可以自定义
构造方法赋值
java类中加载配置文件
ApplicatonContext ac = new ClassPathXmlApplicationContext(spring配置文件名.xml);
注入的类型 xx = (需要强转) ac.getBean(“注入的id”);
或者(两种方式都可以)
注入的类型 xx = ac.getBean("注入id", 注入类型.class);
注意:
一个对象只能有一个实例,否则会抛异常
注解如果没有指定(注解自定义名字)bean的名字,默认为小写开头的类名。(特别注意)
给bean注入集合
相当于value是一个 list 属性
scope作用域
Spring的继承
Spring的继承和java的继承不同,Spring的继承只继承属性,继承后如果子类给属性赋值会覆盖父类的属性,如图就是只有覆盖了父类的name
Spring的继承不在于具体的类,而是对象,即使两个类属性不同,但是子类的属性必须包含父类的属性,这样子类可以在继承后的基础上可以添加其他属性,也可以覆盖父类的属性
Spring的依赖
依赖也是描述bean和bean之间的一种关系,配置依赖之后,被依赖的bean一定先创建,A依赖于B,先创建B再创建A
如图正常是自上而下的执行创建吗,但是添加了依赖,所以先创建user再创建student
Spring的p命名空间
p命名空间是对loC和DI的简化,使用p命名空间可以更少的代码完成bean的配置以及配置之间的依赖注入
p:属性=就可以直接给属性赋值
p:address-ref=相当于把图中address对象的值赋给了student
Spring 注解实现自动装配
配置文件中必须添加:<context:annotation-config/>
<?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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
@Autowired:默认通过byName查找,然后byType,required属性决定调用该属性set方法时值是否可以null。
如果byName,byType都没有找到,也就是Auto wired不能自动装配上属性,则需要通过@Qualifier(value="xxx")
@Resource:先通过baName查找,然后byType,name属性指定一个bean进行注入。
@Nullable:字段标记了这个注解,说明可以为null
byName是找bean的id
byType是找bean匹配的属性
@Autowired(required = true)
@Qualifier(value=" ")
@Resource(name = " ")
@Nullable
Spring注解开发配置(Ioc)
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描包,使包下面的注解生效,base-package扫描的位置-->
<context:component-scan base-package="com.yunhe"/>
<!-- 注解实现自动装配必须添加 -->
<context:annotation-config/>
</beans>
使用注解交由spring管理
以下四个包里的注解意思相同,即,将此类注册为Spring组件
pojo层: @Component
dao层: @Repository
service层: @Service
controller层: @Controller
给属性赋值:@value("12")
可以写在属性上面或者set方法上
xml与注解的区别
xml 更加万能,适用任何场景,维护方便
注解 不是自己的类使用不了,维护相对复杂
建议:
xml 用来管理bean
注解 只负责完成属性注入
注意:
需要扫描相应包下的所有注解,才能生效(需要哪个包扫描哪个包)
使用纯Java方式配置Spring(类似Spring Boot)
实体类
配置文件
测试类
Spring注解开发(Aop)
什么是Aop:
aop是面向切面编程,基于动态代理,底层是对动态代理的封装
aop就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了
非业务代码更加集中,不分散,便于统一管理。
业务代码更加简洁存粹,不参杂其他代码的影响
代码复用,降低耦合度
便于统一管理,维护
切面解析是通过对象,不是类
Spring中aop基于动态代理,JDK动态代理和CGLIB动态代理自动交替使用
- 如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
- 如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
Aop注解:
@Component ,则将该类扫描到 IoC 容器中,即 IoC 管理它的对象。
@Aspect 表示让该类成为切面对象
@Component 表示注入Ioc容器中(xxx Impl也需要添加让aop管理)
@Before 表示方法执行的具体位置和时机
@After 都是通过JoinPoint调用不同的方法
@AfterReturning 表示返回结果可以调用
@AfterThrowing 表示报异常会执行此方法中的内容
Aop使用术语:
Target(目标对象):代理的目标对象,必须由spring进行管理
Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
Aspect(切面):是切入点和通知(引介)的结合
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
使用aop需要三个明确:
谁是切点(切点表达式配置) spring监控的方法有哪些
谁是通知(切面类中的增强方法) 实现书写的好的进行增强方法的类
将切点和通知进行织入配置 在配置文件中将对应方法监控与增强方法的类进行设置
After注解方式(模板)
后置通知也叫最终通知,通过切点表达式对指定方法进行后置增强,在方法执行后增强(无论方法是否执行成功)
通知注解(模板实例)
后置通知也叫最终通知,通过切点表达式对指定方法进行后置增强,在方法执行后增强(无论方法是否执行成功)
//后置通知
@After("execution(* com.yunhe.service.impl.BankServiceImpl.*Money(..))")
public void after() {
System.out.println("原方法执行后增强的方法");
}
等价于xml配置
<aop:after method="after" pointcut="execution(* com.yunhe.service.impl.BankServiceImpl.*Money(..))"></aop:after>
通用切点表达式(Pointcut注解)
相当于把表达式独立出来,需要时调用
当多个切点使用相同切点表达式时可以创建方法使用Pointcut注解书写切点表达式,其他通知方法通过类名.方法名获取切点
在实际开发过程中通常会创建一个单独保存切点表达式的切面类,其他切面类通过类名.方法名使用对应切点,当然也可以为不同功能aop创建不用的切面类将对应功能的切点表达式书写在对应功能的切面类中
@Pointcut("execution(* com.yunhe.service.impl.BankServiceImpl.*Money(..))")
//书写方法使用注解定义通用切点表达式
public void pointcut(){}
//前置通知
// @Before("execution(* com.yunhe.service.impl.BankServiceImpl.*Money(..))")
@Before("MoneyAspect.pointcut()")
//使用类名.配置切点表达式方法名进行获取
public void before() {
System.out.println("原方法执行前增强的方法");
}
切点表达式的书写
语法:execution([修饰符]返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号*代表任意
- 包名与类名之间一个点 . 代表当前包下的类,两个点 ..表示当前包及其子包下的类
- 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
-
注解参数只有一个的时候可以省略value不写,两个参数就必须写了
Spring快速使用入手
spring
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
aop
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
切面
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
JoinPoint
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
spring.xml中配置Aop
<?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/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<!-- 动态扫描 -->
<context:component-scan base-package="org.aop"/>
<!-- 使Aspect注解生效,自动完成创建代理织入切面成为动态代理对象 -->
<aop:aspectj-autoproxy/>
</beans>
切面类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect
@Component
public class LogAspect {
@Pointcut(value = "execution(public * org.aop.UserImpl.*(..))")
//书写方法使用注解定义通用切点表达式
public void pointcut(){}
// @Before("execution(public int org.aop.UserImpl.*(..))")
// 只有一个参数的时候value可以省略
// 使用切入点注解可以不写LogAspect.直接写pointcut
@Before(value = "LogAspect.pointcut")
public void before(JoinPoint joinPoint){
//获取方法名
String name = joinPoint.getSignature().getName();
//获取参数
String args = Arrays.toString(joinPoint.getArgs());
System.out.println(name+"方法的参数是"+args);
}
@After("execution(public int org.aop.UserImpl.*(..))")
public void after(JoinPoint joinPoint){
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法执行完毕");
}
// 环绕通知
@Around("execution( * com.yunhe.dao.*.*(..))")
//参数需要ProceedingJoinPoint,如果不写Proceeding就不能调用proceed方法
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知");
Object[] args = joinPoint.getArgs();
Object proceed = joinPoint.proceed(args);
System.out.println("环绕通知");
return proceed;
}
// 抛出异常执行
// throwing = "e"和形式参数列表的Exception e的变量名必须相同
//有两个参数value不能省略
@AfterThrowing(value = "LogAspect.pointcut",throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Exception e){
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法的异常是"+e);
}
//Object result是return返回的结果
//Object result和输出结果("方法结果是"+result)它们俩命名必须一样
//有两个参数value不能省略
@AfterReturning(value = "execution(public int org.aop.UserImpl.*(..))",returning = "result")
//如果不写execution执行位置会报错如下
//@org.aspectj.lang.annotation.AfterReturning(pointcut=, value=, argNames=, returning=) cannot be an AspectJ annotation
public void afterReturning(JoinPoint joinPoint,Object result){
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法结果是"+result);
}
}
测试
public class AopTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("SpringAop.xml");
User user = (User) ac.getBean("userImpl");
user.adds(1,2);
}
}
接口
public interface User {
public void select();
public void add();
public void delete();
public void update();
public int adds(int i1, int i2);
}
实现类
需要使用@Component注解
import org.springframework.stereotype.Component;
@Component
public class UserImpl implements User {
@Override
public void select() {
System.out.println("查");
}
@Override
public void add() {
System.out.println("增");
}
@Override
public void delete() {
System.out.println("删");
}
@Override
public void update() {
System.out.println("改");
}
@Override
public int adds(int i1, int i2) {
// 使用动态代理后就不需要每一个都写了,减少代码耦合度,增加代码复用性
// System.out.println("adds方法参数是"+i1+i2);
System.out.println(i1+"+"+i2);
return i1+i2;
}
}
动态代理
在目标类源代码不改变的情况下,增加功能
减少代码耦合度,增加代码复用性
解耦合,让业务功能和日志,事务非业务功能分离
jdk动态代理与cglib动态代理的区别:
jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包或者在pom.xml配置文件中加入定位spring-core,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
JDK动态代理
基于接口实现代理技术
工具类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class ProxyInvocationHandler implements InvocationHandler {
//需要代理的目标(一个实现类)
Object target;
//设置需要代理的目标
public void setTarget(Object target) {
this.target = target;
}
//动态生成此目标的代理对象(抽象接口) 等同于接收target(实现类)返回target实现的抽象接口
public Object getProxy(){
//反射机制
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
//生成的代理对象(target实现的接口)可以执行该接口相应的方法 method是此invoke中调用过的方法
//proxy是当前代理对象User
//method是代理实现对象的方法UserImpl
//args是方法执行的实参
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"⽅法的参数是"+Arrays.toString(args));
Object result = method.invoke(target, args);
System.out.println(method.getName()+"的结果是"+result);
return result;
}
}
测试
public class UserTest {
public static void main(String[] args) {
ProxyInvocationHandler pih = new ProxyInvocationHandler();
UserImpl userService = new UserImpl();
//扔进实现类
pih.setTarget(userService);
//返回代理对象(抽象接口)
User proxy = (User) pih.getProxy();
//代理对象调用实现类相应方法
proxy.add();//增
proxy.adds(1,1);
}
}
接口
public interface User {
public void select();
public void add();
public void delete();
public void update();
public int adds(int i1,int i2);
}
实现类
public class UserImpl implements User{
@Override
public void select() {
System.out.println("查");
}
@Override
public void add() {
System.out.println("增");
}
@Override
public void delete() {
System.out.println("删");
}
@Override
public void update() {
System.out.println("改");
}
@Override
public int adds(int i1, int i2) {
// 使用动态代理后就不需要每一个都写了,减少代码耦合度,增加代码复用性
// 如下实例是日志
// System.out.println("adds方法参数是"+i1+i2);
System.out.println(i1+"+"+i2);
return i1+i2;
}
}
CJLIB动态代理
基于父类的动态代理技术
创建被代理的目标类
public class Target {
public void method() {
System.out.println("原始方法执行");
}
public void method1(int i) {
System.out.println("原始方法执行"+i);
}
}
创建增强器(用于创建增强子类的工具)
Target target = new Target(); //创建目标对象
Enhancer enhancer = new Enhancer(); //创建增强器
enhancer.setSuperclass(Target.class); //设置父类
enhancer.setCallback(new MethodInterceptor() { //设置回调
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置代码增强2....");
Object invoke = method.invoke(target, objects);
System.out.println("后置代码增强2....");
return invoke;
}
});
//cglib动态代理原理
//创建对应目标类的子类通过类似于重写的方式将增强代码书写
调用增强器创对应代理对象执行方法
Target proxy = (Target) enhancer.create(); //创建代理对象
proxy.method();
Java- lombok注解:
jar包依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>
@Data注解中包含了(@Getter、@Setter、@toString、@equalsAndHashCode)
@AllArgsConstructor是有参构造
@NoArgsConstructor是无参构造
默认有无参,如果加有参注解,无参就没了
重写方法后注解生成的会被覆盖
建议 无参有参注解都加
常用jar包坐标
<!-- spring进行jdbc操作的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<!-- spring进行事务管理的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- 连接mysql数据库的依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- 数据源C3P0管理依赖 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- 数据源DRUID管理依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- 单元测试依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- spring进行测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- spring进行web开发依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- springMVC核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!-- jsp依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<!-- json转化依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<!-- spring进行aop 织入依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!-- 文件上传依赖 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
</dependencies>
Spring报错问题
Caused by: java.lang.IllegalArgumentException:
warning no match for this type name: com.yunhe.dao [Xlint:invalidAbsoluteTypeName]
解决方法:
切入点表达式写错了,注意路径
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException:
Bean named 'bankServiceImpl' is expected to be of type 'com.yunhe.dao.BankServiceImpl' but was actually of type 'com.sun.proxy.$Proxy14'
解决方法:
把<aop:aspectj-autoproxy/> 修改成<aop:aspectj-autoproxy proxy-target-class="true"/>