spring
简介
轻量级的控制反转IOC和面向切面编程AOP的框架。
7大模块
spring AOP ,spring Core , spring WebMVC , spring Web , spring Context , spring ORM , spring DAO
扩展
spring boot
构建,快速开发微服务,约定大于配置
spring Cloud
spring Cloud Data Flow
IOC控制反转
先执行配置文件,applicationContext.xml实例化
实例化对象Person为变量名person,对person的name,age进行赋值,使用ref属性引用Spring容器中创建好的对象
<bean id="person" class="com.example.chapter_dog.Person">
<property name="name" value="林师傅"/>
<property name="age" value="43"/>
<property name="dog" ref="dog"/>
</bean>
<bean id="dog" class="com.example.chapter_dog.Dog">
<property name="name" value="小黑"/>
<property name="brand" value="贵宾"/>
</bean>
在测试类使用ApplicationContext获取spring的上下文对象(拿到容器),spring代替你new对象
通过修改xml修改程序
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person)ac.getBean("person");
person.porting();
控制反转
依赖注入:利用set方法来注入,xml中使用set方法实例化
对象由spring创建,管理,装配
bean无参构造
配置文件加载时,容器就已经初始化对象
<bean id="person1" class="com.example.chapter_dog.Person">
</bean>
bean有参构造
了解:别名
<!--name:别名-->
<bean id="person1" class="com.example.chapter_dog.Person" name="aaaa">
<constructor-arg index="0" value="wangshifu"/>
<constructor-arg index="1" value="20"/>
</bean>
或者
<bean id="person1" class="com.example.chapter_dog.Person">
<constructor-arg name="name" value="wangshifu"/>
<constructor-arg name="age" value="20"/>
</bean>
或者(不推荐)
<bean id="person1" class="com.example.chapter_dog.Person">
<constructor-arg type="java.lang.String" value="wangshifu"/>
<constructor-arg type="java.lang.String" value="20"/>
</bean>
import
使用import将所有人的xml合并为一个
<import resource="beans.xml"/>
<import resource="beans1.xml"/>
依赖注入
构造器注入
依赖:bean对象创建依赖容器
注入:bean所有属性由容器注入
set方式注入
<property name="name" value="wang=rui"/>
<property name="address" ref="address"/>
<!--数组-->
<property name="books">
<array>
<value>水浒传</value>
<value>三国</value>
<value>西游记</value>
</array>
</property>
<!--列表-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>玩游戏</value>
</list>
</property>
<!-- Map-->
<property name="card">
<map>
<entry key="学号" value="123456789"/>
<entry key="身份证" value="78916534864564165"/>
</map>
</property>
<!-- Set-->
<property name="games">
<set>
<value>LOL</value>
<value>Pokemmo</value>
</set>
</property>
<!-- null-->
<property name="wife">
<null></null>
</property>
<property name="info">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
拓展方式注入
p命名空间(set注入)和c命名空间(构造器注入constructor-arg)注入
导入xml约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
<bean id="stu1" class="com.example.spring_di_set.Student" p:name="wangrui"/>
<bean id="stu2" class="com.example.spring_di_set.Student" c:name="wangrui"/>
bean作用域
singleton单例模式
spring默认的模式,bean的每次实例化共享一个对象
<bean id="stu1" class="com.example.spring_di_set.Student" p:name="wangrui" scope="singleton"/>
prototype原型模式
每次从容器get,都会产生一个新对象
<bean id="stu1" class="com.example.spring_di_set.Student" p:name="wangrui" scope="prototype"/>
bean自动装配
byName自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid,保证id值唯一
<bean id="stu3" class="com.example.spring_di_set.Student" autowire="byName"></bean>
byType自动在容器上下文中查找,和自己对象属性相同的beanid,保证所有bean的class唯一,保证属性类型一样
<bean id="stu3" class="com.example.spring_di_set.Student" autowire="byType"></bean>
注解自动装配
注解使用反射实现
1.导入context约束
<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"
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/context.xsd
">
2.@Autowired直接在属性上使用
public void Student{
@Autowired
private Address address;
}
开启注解的支持context:annotation-config
<context:annotation-config></context:annotation-config>
<bean id="stu" class="com.example.spring_di_set.Student"/>
<bean id="address" class="com.example.spring_di_set.Address"/>
3.如果有多个bean对象,可以和@Qualifier配合使用,指定唯一bean对象,value值为bean对象id
public void Student{
@Autowired
@Qualifier(value = "address111")
}
4.@Resource注解
也是自动装配
@Autowired通过byType实现
@Resource默认通过byName实现,找不到则通过byType实现
5.扫描指定的包,使用@Component
@Value("wang")//赋值
private String name;
配合@Component使用
<context:component-scan base-package="com.qst.ch_111.dao"/>
6.@Component衍生注解
- dao层常用@Repository
- service层常用@Service
- controller层常用@Controller
祝贺四个功能一样,代表类注册到spring,装配bean
7.在类上添加注解@Scope(“singleton”)设置作用域
总结
- 使用注解需要导入spring-aop.jar
- 使用注解需要导入约束context
- 使用注解维护困难
- xml适用于任何场合
使用java配置Spring
@Configuration代表这是一个配置类,类似于applicationContext.xml
@bean:注册一个bean,相当于bean.xml中的一个,方法名=id,返回值=class
@Configuration
@ComponentScan("com.example.spring_di_set")//扫描包
public class WangConfig {
@Bean
public Student getStudent(){
return new Student();
}
}
测试配置类
public class MyTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(WangConfig.class);
User user = ac.getBean("getUser",User.class);
System.out.println(user.toString());
}
}
实体类,使用@Value赋值
如果扫描包可以不用@bean
java配置的两种方式:配置类+@bean 或者 配置类+扫描包
AOP
代理模式
静态代理
代理模式是springAOP的底层
分类:
- 静态代理
- 动态代理
客户找中介租房子例子:
1.真实角色(房东)
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
2.代理角色(中介)
public class Proxy implements Rent{
Host host = new Host();
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
//房东出租房子
@Override
public void rent() {
look();
host.rent();
}
//看房子(代理功能扩展)
public void look(){
System.out.println("带客户看房");
}
}
3.相同接口(租房行为)
public interface Rent {
void rent();
}
4.客户(人)
public class People {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
代理模式好处:
是真实角色操作更加纯粹,
公共操作交给代理,业务分工,
公共业务扩展是,方便集中管理
缺点:
一个真实角色产生一个代理角色,开发效率低
动态代理
动态代理底层是反射
反射Reflection
反射就是获取类的结构,即获取Class(不是class),可以在程序运行中改变某些条件
//创建Class方式一
Class c1 = person.getClass();
//创建Class方式二
Class c2 = Class.forName("com.example.reflection.Student");
//创建Class方式三
Class c3 = Student.class;
获取类运行时的结构(属性,方法,,,)
//获得类的名字
System.out.println(c1.getName());//包名+类名
System.out.println(c1.getSimpleName());//类名
//获得类的属性
Field[] fields = c1.getFields();//获得类的public属性
for (Field field : fields) {
System.out.println("public属性"+field);
}
fields=c1.getDeclaredFields();//获得所有属性
for (Field field : fields) {
System.out.println("属性"+field);
}
//获得指定属性
Field name = c1.getField("name");
System.out.println(name);
//获得类的方法
Method[] methods = c1.getMethods();//获得本类及其父类的public方法
for (Method method : methods) {
System.out.println("public方法"+method);
}
methods=c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println("所有"+method);
}
//获得指定方法
Method getName = c1.getMethod("getName", null);
System.out.println(getName);
Method setName = c1.getMethod("setName", String.class);
System.out.println(setName);
//获得构造器
Constructor[] constructors = c1.getConstructors();//public
for (Constructor constructor : constructors) {
System.out.println(constructors);
}
constructors=c1.getDeclaredConstructors();//所有
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//获得指定构造器
Constructor declaredConstructor = c1.getConstructor(String.class);
System.out.println(declaredConstructor);
类加载器ClassLoader
1、分类:
- 启动(Bookstrap)类加载器,ClassLoader,
- 扩展(Extension)类加载器,由ExtClassLoader实现
- 系统(System)类加载器,由AppClassLoader实现
2、双亲委派机制
类加载器有任务委托父类,依次递归,到启动类加载器(根加载器)
public class Test_ClassLoader {
public static void main(String[] args) throws ClassNotFoundException {
//类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//扩展加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//根加载器
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//测试当前类是谁加载的
ClassLoader classLoader = Class.forName("com.example.reflection.Test_ClassLoader").getClassLoader();
System.out.println(classLoader);
//测试JDK内置类谁加载的
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);
//获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
}
输出结果
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成得到,不是直接写好的
动态代理代理的是接口,getProxy返回值要转为代理的接口类型
分类:
- 基于接口的动态代理,JDK代理
- 基于类的动态代理,cglib代理
- java字节码实现,javassist
动态代理例子:
使用动态生成代理类,只需传入不同接口就可切换要代理的接口,在Test切换接口的实现类就可切换代理的类。
调用处理程序
public class Proxy_InvocationHandler implements InvocationHandler { //动态代理,InvocationHandler是代理处理程序
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类,三个参数(获取类加载器,获取接口,InvocationHandler)
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
@Override
//处理代理实例,并返回结果,在生成代理类时自动执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName()); //method.getName:通过反射获取执行的方法的名字
Object invoke = method.invoke(target, args);
return invoke;
}
//代理类的扩展功能,日志功能
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
newProxyInstance方法的三个参数:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
- interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
- h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
测试
public class Client {
public static void main(String[] args) {
Host host =new Host(); //接口的实现类(真实角色),可切换实现类来切换代理的对象
//通过调用程序处理角色来处理要调用的接口
Proxy_InvocationHandler pih = new Proxy_InvocationHandler();
pih.setTarget(host); //传入代理要代理的接口
Rent proxy = (Rent) pih.getProxy(); //创建代理类,改变类型为接口类型
proxy.rent(); //测试接口的方法
}
}
动态代理好处:
- 一个动态代理代理得到是一个接口,一般就是对应的一类业务
- 一个动态代理可以代理多个类,只要他们实现了同一接口
AOP
aop实现方式一:使用Spring的API接口实现
可以在实现MethodBeforeAdvice的类中使用反射,获取类的结构
1.xml配置文件,导入aop约束,
2.log、afterlog是编写的类实现BeforeAdvice和AfterAdvice
log
afterlog
3.测试
4.结果
aop实现方式二:自定义实现AOP(切面定义)
before:前置通知、afterReturning:后置通知、around:环绕通知、afterThrowing:异常通知、after:最终通知
实体类
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("添加");
}
@Override
public void delete() {
System.out.println("删除");
}
}
applicationContext.xml配置
<bean id="log_1" class="com.example.spring_aop.log.Log_1"/>
<bean id="userService" class="com.example.spring_aop.service.UserServiceImpl"/>
<!-- 方式二:自定义-->
<aop:config>
<!-- 切面-->
<aop:aspect id="aspect" ref="log_1">
<!-- 切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.example.spring_aop.service.*.*(..))"/>
<!-- 通知-->
<aop:before method="myBefore" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
自定义通知
public class Log_1 {
public void myBefore(){
System.out.println("===前置日志===");
}
}
测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = ac.getBean("userService", UserService.class);
userService.add();
}
}
结果
aop实现方式三:使用注解实现AOP
实体类
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("添加");
}
@Override
public void delete() {
System.out.println("删除");
}
}
xml配置
<bean id="log" class="com.example.spring_aop.log.Log"/>
<!-- 方式三:使用注解-->
<!-- proxy-target-class默认是false,代表jdk代理,true是cglib代理-->
<aop:aspectj-autoproxy/>
通知
@Aspect
public class Log {
@Pointcut("execution(* com.example.spring_aop.service.*.*(..))")
private void pointcut(){};
@Before("pointcut()")
public void myBefore(){
System.out.println("前置日志===");
}
}
结果