Spring
1、spring
1.1、优点
- spring是一个开源的免费框架
- spring是一个轻量级、非入侵式的框架
- 控制反转(ioc),面向切面编程(aop)
- 支持事务的处理,对框架整合的支持
官方文档
https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#beans-dependencies
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
1.2、组成
1.3、拓展
现代化的java开发,就是基于spring的开发!
- spring boot
- 一个快速开发的脚手架
- 基于spring boot可以快速开发单个微服务
- 约定大于配置!
- spring cloud
- spring cloud是基于spring boot实现的
2、IOC理论推导
6、依赖注入
6.1、构造器注入
6.2、Set方式注入 【重点】
6.3、拓展方式注入
6.3.1、p命名空间和c命名空间 bean
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间, 通过属性注入:property-->
<bean id="user" class="com.kuang.pojo.User" p:name="鑫满" p:age="22"></bean>
<!--c命名空间注入,通过构造器注入:construct-args-->
<bean id="user2" class="com.kuang.pojo.User" c:name="满满" c:age="88"></bean>
</beans>
6.3.2、实体类User
package com.kuang.pojo;
public class User {
private String name;
private int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
6.3.3、测试
@Test
public void test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2", User.class);
System.out.println(user);
}
**注意点:**p命名空间和c命名空间不能直接使用,需要导入xml约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
6.4 、bean作用域
| Scope | Description |
|---|---|
| singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
| prototype | Scopes a single bean definition to any number of object instances. |
| request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext. |
| session | Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext. |
| application | Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext. |
| websocket | Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext. |
1、代理模式(spring默认机制)
<bean id="user2" class="com.kuang.pojo.User" c:name="满满" c:age="88" scope="singleton" />
2、原型模式:每次从容器中get的时候,都会产生一个新的对象
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
3、request、session、application;这些个只能在web开发中使用到!
7、Bean的自动装配
- 自动装配是Spring满足bean依赖一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性
在Spring中三种装配的方式
- 在xml中显示的配置
- 在java中显示配置
- 隐式的自动装配bean 【重点】
7.1、byName自动装配
<!--byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid-->
<bean id="people" class="com.kuang.pojo.People" autowire="byName">
<property name="name" value="小缘"/>
</bean>
7.2、byType自动装配
<!--byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean-->
<bean id="people2" class="com.kuang.pojo.People" autowire="byType">
<property name="name" value="小缘"/>
</bean>
小结:
- byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
- byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!
7.3、使用注解实现自动装配
jdk1.5支持的注解,spring2.5就支持注解
要使用注解须知:
-
导入约束。context约束
-
配置注解支持
<?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
直接在属性上使用即可!也可以在set方式上使用
使用autowired我们可以不用编写Set方式,前提是你这个自动装配的属性在IOC(spring)容器中存在,且符合名字byName
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为null public @interface Autowired { boolean required() default true; }// 如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired(required = false)如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value="xxx")去配置@Autowired的使用,指定一个位于的bean对象注入!
@Resource注解
小结:
@Autowired和@Resource的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired通过byType的方式实现,而且必须要求这个对象存在【常用】
- @Resource默认通过byName的方式,如果找到名字,则通过byType实现!如果两个都找不到的情况下,就报错
- 执行顺序不同:@Autowired通过byType的方式实现。 @Resource默认通过byName的方式。
8、使用注解开发
在spring4之后,要使用注解开发, 必须要保证aop包导入
-
bean
-
属性注入
@component public class user { public String name; @value("kuang") public void setName(String name) { this.name = name; } } -
衍生注解
-
@component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层
- dao 【@Repository】
- service 【@Service】
- controller 【@Controller】
这四个注解功能都是代表某个类注册到spring中
-
-
自动装配
- Autowired自动装配通过类型。名字 如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx") - @Nullable 字段标记这个注解,说明这个字段可以为null - @Resource 自动装配通过名字。类型 -
作用域
@Component @Scope("prototype") public class user { public String name; @value("kuang") public void setName(String name) { this.name = name; } }
9、使用java的方式配置spring
10、代理模式
代码模式就是SpringAOP的底层 【SpringAop 和 SpringMvc】
代理模式的分类:
- 静态代理
- 动态代理
10.1、静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤
-
接口
// 租房 public interface Rent { public void rent(); } -
真实角色
// 房东 // 房租实现租房的动作 public class Host implements Rent{ public void rent() { System.out.println("房东要出租房子"); } } -
代理角色
package com.kuang.demo01; // 代理 // 代理实现租房接口 public class Proxy implements Rent { private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } public void rent() { seeHouse(); // 通过房东调用租房放 host.rent(); heTong(); } // 看房 public void seeHouse(){ System.out.println("中介带我看房"); } // 签合同 public void heTong(){ System.out.println("中介和我签合同"); } } -
客户访问代理角色
package com.kuang.demo01; public class Client { public static void main(String[] args) { // 创建一个房东 Host host = new Host(); // 产生一个中介;用于代理房东出租房子 Proxy proxy = new Proxy(host); // 中介把房子出租给我 proxy.rent(); } }
代理模式的好处:
- 可以使真实觉得的操作更加纯粹!不用去关注一些公共的业务
- 公共也就交给代理角色!实现了业务的分工
- 公共业务发生拓展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍~开发效率会变低~
10.1.2、加深理解
10.2、动态代理
- 动态代理和静态代理角色一样
- 动态代理的类是动态生成的,不是我们直接写好的!
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口 ---JDK动态代理 【**】
- 基于类:cglib
- java字节码实现:javasist
- Proxy:代理
- InvocationHandler:调用处理程序
动态代理的好处:
- 可以使真实觉得的操作更加纯粹!不用去关注一些公共的业务
- 公共也就交给代理角色!实现了业务的分工!
- 公共业务发生拓展的时候,方便集中管理
- 一个动态代理类带来的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
-
接口
// 租房 public interface Rent { public void rent(); } -
代理程序
package com.kuang.demo03; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { // 被代理的接口 private Rent rent; public void setRent(Rent rent) { this.rent = rent; } // 生成得到代理类 public Object getProxy(){ // Proxy.newProxyInstance:生成代理对象 // this.getClass().getClassLoader(): 加载到类在那个位置 // rent.getClass().getInterfaces:代理的是那个接口 // this:代表InvocationHandler,这里是自己,因为我们的ProxyInvocationHandler实现了InvocationHandler return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this); } // 处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 用invoke实现rent方法 // 动态代理的本质,就是使用反射机制实现 Object result = method.invoke(rent, args); return result; } } -
访问真实角色
package com.kuang.demo03; public class Client { public static void main(String[] args) { // 真实对象 Host host = new Host(); // 代理对象 现在没有代理对象,我们找代码方法 ProxyInvocationHandler pih = new ProxyInvocationHandler(); // 通过调用程序处理真实角色host,来处理我们要调用的接口对象 // 设置要代理的对象 pih.setRent(host); // 这里的proxy就是动态生成的,并没有写 Rent proxy = (Rent) pih.getProxy(); proxy.rent(); } }封装代理程序为工具类
package com.kuang.demo04; import com.kuang.demo03.Rent; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { // 被代理的接口 private Object target; public void setTarget(Object target) { this.target = target; } // 生成得到代理类 public Object getProxy() { // Proxy.newProxyInstance:生成代理对象 // this.getClass().getClassLoader(): 加载到类在那个位置 // rent.getClass().getInterfaces:代理的是那个接口 // this:代表InvocationHandler,这里是自己,因为我们的ProxyInvocationHandler实现了InvocationHandler return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } // 处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 用invoke实现target方法 // 动态代理的本质,就是使用反射机制实现 log(method.getName()); Object result = method.invoke(target, args); return result; } // log方法 public void log(String msg) { System.out.println("【Debug】这一个" + msg + "方法"); } }总结
动态代理。就两个类。
-
生成代理类Proxy.newProxyInvocation(参数1.参数2,参数3)
-
参数1:表名你要用那个类加载器去加载生成的代理类。(这个是JVM类加载的知识,可以去了解一下,挺简单)。
-
参数2:说明你要生成的代理类的接口。
-
参数3:实现了InvocationHandle的类,这个类只有一个方法需要你要实现它。
-
invoke(Object proxy, Method method, Object【】 args) {
- 这个方法第一个参数,是生成的代理类,目前没发现用处,不管它。
- 第二个参数,是执行的方法(利用反射的原理,可以去看反射,也很简单。)
- 第三个参数,是执行某方法需要的参数。
- 第二个第三个参数解释执行的方法意思是:代理类不是要代理某个对象么,然后增强里面的方法么,指得就是这个方法,代理类会为几乎所有方法都增强,除非你在这里做判断。 返回值,是执行这个方法所返回的值。 然后你要去执行方法,就是用第二参数的invoke(obj,args);第一个参数是你要增强的对象。第二个是参数。object是你返回的类型Object object= method.invoke(obj,args); }
-
11、AOP
11.3、使用Spring实现Aop
【重点】使用Aop植入,需要导入一个依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
- 方式一:使用原生spring API接口 【主要spring API接口实现】
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
// method: 要执行的目标对象的方法
// args: 参数
// target: 目标对象
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了!" );
}
}
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
// returnValue; 返回值
public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+ method.getName() + "方法,返回结果为:"+ returnValue);
}
}
<!--注册bean-->
<bean id="userService" class="com.kuang.service.UserServiceImpl"/>
<bean id="log" class="com.kuang.log.Log"/>
<bean id="afterlog" class="com.kuang.log.AfterLog"/>
<!--方式一:使用原生spring API接口-->
<!--配置aop,需要导入aop的约束-->
<aop:config>
<!--切入点: expression: 表达式,execution(要执行的位置! * * * * *)-->
<aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<!--要执环绕增强-->
<!--把log这个切入点,切入到pointcut类-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
-
方式二:自定义来实现AOP 【主要定义切面】
package com.kuang.diy; public class DiyPointcut { public void Before(){ System.out.println("===方法执行前==="); } public void After(){ System.out.println("===方法执行后==="); } }<!--注册bean--> <bean id="diy" class="com.kuang.diy.DiyPointcut"/> <aop:config> <aop:aspect ref="diy"> <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/> <aop:before method="Before" pointcut-ref="pointcut"/> <aop:after method="After" pointcut-ref="pointcut"/> </aop:aspect> </aop:config> -
方式三:注解方式
package com.kuang.diy; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; // @Aspect 标记这个类是一个切面 @Aspect public class AntowiredAspet { @Before("execution(* com.kuang.service.UserServiceImpl.*(..))") public void Before(){ System.out.println("===注解方式:方法执行前==="); } @After("execution(* com.kuang.service.UserServiceImpl.*(..))") public void After(){ System.out.println("===注解方式:方法执行后==="); } // 在环绕增强中,我们可以定义一个参数,代表我们要获取处理切入的点 @Around("execution(* com.kuang.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint pj) throws Throwable { System.out.println("===注解方式:环绕前==="); Signature signature = pj.getSignature(); // 获取签名 System.out.println("signature" + signature); Object proceed = pj.proceed(); System.out.println("===注解方式:环绕后==="); System.out.println(proceed); } }<!--方式三 注解--> <bean id="antowiredAspet" class="com.kuang.diy.AntowiredAspet"/> <aop:aspectj-autoproxy/>
-
测试代码
import com.kuang.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); // 动态代理代理的是接口 UserService userService = context.getBean("userService", UserService.class); userService.add(); } }