Spring6学习及复习笔记
1、快速入门认识
通过编写xml配置文件来创建对象,读取bean标签的id值和class值来创建。
之后再通过对象调用相关的方法(这里实际上用到的是反射机制)
对象会存放到Map集合中
大致反射思路如下:(这里只是模拟,并非真的就这几行实现)
2、整合log4j2日志框架
引入依赖,创建配置文件log4j2.xml 这样便可自动生成日志
手动写日志:
Logger logger=LoggerFactory.getLogger(testUser.class);
logger.info("手动写日志成功了");
3、IOC
一、IOC容器
IOC即为控制反转,使用IOC容器管理bean对象,设计出更加松耦合的程序
用它来管理对象的实例化和初始化,以及对象间的依赖关系。
对象用Map集合存放
DI,即为依赖注入,实现了控制反转这一思想
一般使用set或构造注入
二、XML管理bean
1、获取bean
<bean id="user" class="com.iocxml.User"></bean>
ApplicationContext context=new ClassPathXmlApplicationContext(bean.xml);
User user1=(User)context.getBean("user");//根据id获取bean
User user2=(User)context.getBean(User.class);//根据类型获取bean
User user3=(User)context.getBean("user",User.class);//根据id和类型获取bean
注意,获取时指定类型bean只能有一个,如果同一个类有多个bean会报错。
一般都只根据id获取,确保每个bean的id不一样
这里还可以配置接口的实现类,然后获取对象时通过接口的类名来获取,但实现类必须唯一。
2、setter注入
确保属性有set方法和构造方法
//set注入
<bean id="book" class="com.book">
<property name="name" value="图书名称"></property>
<property name="author" value="图书作者"></property>
</bean>
//构造器注入
<bean id="book1" class="com.book">
<constructor-arg name="name" value="图书名称"></constructor-arg>
<constructor-arg name="author" value="图书作者"></constructor-arg>
</bean>
特殊值处理
- 字面量赋值。String a="33a"
- null。 使用标签<null/>
- xml实体。 对字符转义 < :< >:>
- CDATA节 用来写特殊符号
3、对象类型赋值
//引入外部bean
<bean id="dept" class="com.dept">
<property name="dname" value="部门属性"></property>
</bean>
<bean id="emp" class="com.emp">
<property name="ename" value="员工名字"></property>
<property name="age" value="50"></property>
<property name="dept" ref="dept"></property>
</bean>
//内部bean
<bean id="emp2" class="com.emp">
<property name="ename" value="员工名字"></property>
<property name="age" value="50"></property>
<property name="dept" >
<bean id="dept2" class="com.dept">
<property name="dname" value="部门属性"></property>
</bean>
</property>
</bean>
//级联属性赋值(少用)
<bean id="dept3" class="com.dept">
</bean>
<bean id="emp3" class="com.emp">
<property name="ename" value="员工名字"></property>
<property name="age" value="50"></property>
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="部门属性"></property>
</bean>
4、数组类型属性注入
<bean id="dept" class="com.dept">
<property name="dname" value="部门属性"></property>
</bean>
<bean id="emp" class="com.emp">
<property name="ename" value="员工名字"></property>
<property name="age" value="50"></property>
<property name="dept" ref="dept"></property>
<property name="hobby" >
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
</bean>
5、集合类型属性注入
List集合属性注入
<bean id="dept" class="com.dept">
<property name="dname" value="部门属性"></property>
<property name="empList">
<list>
<ref bean="emp2"></ref>
</list>
</property>
</bean>
Map集合属性注入
<bean id="dept" class="com.dept">
<property name="dname" value="部门属性"></property>
<property name="empMap">
<map>
<entry>
<key>
<value>1</value>
</key>
<ref bean="emp2"></ref>
</entry>
</map>
</property>
</bean>
引用集合类型的bean,使用util
<property name="empList" ref="empList"></property>
<util:list id=empList>
//里面写集合
<ref bean="emp2"></ref>
</util:list>
6、引入外部属性文件
引用依赖,再创建properties文件。
7、bean的作用域
8、bean的生命周期
<bean id="user" class="com.User" init-method="initMethod" destory-method="destoryMethod">
<property name="name" value="名字"></property>
</bean>
销毁用context.close()
//后置处理器使用,要实现BeanPostProcess接口
9、FactoryBean
配置MyFactoryBean会生成User对象
10、自动注入
三、注解管理bean
1、注解初识
不提供value默认找首字母小写的同类名id
2、Autowired
默认根据类型匹配
@AutoWired //属性注入
private Service service;
@Autowired //set方法注入
public void setUserController(UserService userservice)
{ this.userservice=userservice;}
@Autowired //构造方法注入
public UserController(UserService userservice)
{ this.userservice=userservice;}
//形参注入
public UserController( @Autowired UserService userservice)
{ this.userservice=userservice;}
@AutoWired
@Qualifier(value="userServiceFirst") //联合使用,根据名称进行注入
private Service service;
3、Resource
4、全注解开发
使用配置类来替代配置文件
四、手写IOC
1、Java反射
Car类
package reflect;public class Car {private String name;private int age;private String color;private void run(){System.out.println("私有方法run......");}public Car() {}public Car(String name, int age, String color) {this.name = name;this.age = age;this.color = color;}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;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}
}
TestCar,获取类并实例化对象
package reflect;import org.junit.Test;public class TestCar {//获取class对象的三种方式@Testpublic void test1() throws Exception {//方式一:调用运行时类的属性.classClass carClass = Car.class;System.out.println(carClass);//方式二:通过运行时类的对象,调用getClass()Class carClass1 = new Car().getClass();System.out.println(carClass1);//方式三:调用Class的静态方法:forName(String classPath)Class carClass2 = Class.forName("reflect.Car");System.out.println(carClass2);//实例化Car car=(Car)carClass.getDeclaredConstructor().newInstance();System.out.println(car);}
}
获取构造方法,并设置对象参数
@Testpublic void test2() throws Exception {Class carClass = Car.class;//获取所有构造方法包括private修饰的Constructor[] constructors=carClass.getDeclaredConstructors();for(Constructor constructor:constructors){System.out.println(constructor);}// 获取指定的构造方法Constructor constructor=carClass.getDeclaredConstructor( String.class,int.class,String.class);constructor.setAccessible(true);//解除私有限定Car car=(Car)constructor.newInstance("宝马",10,"红色");System.out.println(car);}
获取属性并设置
@Testpublic void test3() throws Exception {//获取所有属性包括private修饰的Class carClass = Car.class;Car car=(Car)carClass.getDeclaredConstructor().newInstance();Field[] fields=carClass.getDeclaredFields();for(Field field:fields){if(field.getName().equals("name")){field.setAccessible(true);//解除私有限定field.set(car,"奔驰");}// System.out.println(field);System.out.println(car);}}
获取方法得到返回值
@Testpublic void test4() throws Exception {Car car=new Car("奔驰",10,"红色");Class carClass = car.getClass();Method[] methods=carClass.getDeclaredMethods();for(Method method:methods){if(method.getName().equals("run")){method.setAccessible(true);//解除私有限定method.invoke(car);}else if(method.getName().equals("toString")){method.setAccessible(true);//解除私有限定String str=(String)method.invoke(car);System.out.println(str);}}}
2、手写IOC
创建两个注解,bean和di,分别用于创建对象和注入属性
package com.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
package com.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
实现AnnotationApplicationContext
package com.bean;public interface ApplicationContext {Object getBean(Class clazz);
}
package com.bean;import com.anno.Bean;import java.io.File;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;public class AnnotationApplicationContext implements ApplicationContext {private Map<Class,Object> beanMap = new HashMap<>();private String rootpath;@Overridepublic Object getBean(Class clazz) {return beanMap.get(clazz);}public AnnotationApplicationContext(String basepackage){//把包名转化为路径名String packagepath= basepackage.replaceAll("\\.","\\\\");try {//获取当前线程的类加载器,然后获取所有资源的路径Enumeration<URL> urls=Thread.currentThread().getContextClassLoader().getResources(packagepath);//遍历路径while (urls.hasMoreElements()){//获取路径URL url=urls.nextElement();//解码String FilePath= URLDecoder.decode(url.getFile(),"UTF-8");//获取项目的根路径rootpath=FilePath.substring(0,FilePath.length()-packagepath.length());//加载beanloadBean(new File(FilePath));}}catch (Exception e){e.printStackTrace();}}private void loadBean(File file) throws Exception {//如果是文件夹,就进入文件夹if(file.isDirectory()){//获取文件夹下的所有文件File[] files=file.listFiles();//如果文件夹下没有文件,就返回if(files==null||files.length==0){return;}//遍历文件夹下的所有文件for (File child:files){//如果是文件夹,就递归进入文件夹加载if(child.isDirectory())loadBean(child);else {//如果是文件,就加载bean//获取类的路径String pathwithclass=child.getAbsolutePath().substring(rootpath.length()-1);//如果是class文件,就加载beanif(pathwithclass.contains(".class")){//把路径转化为包名String allname=pathwithclass.replaceAll("\\\\",".").replace(".class","");Class<?> clazz=Class.forName(allname);//不是接口if(!clazz.isInterface()){Bean annotation=clazz.getAnnotation(Bean.class);//是beanif(annotation!=null){Object instance =clazz.getConstructor().newInstance();//是否实现了接口if(clazz.getInterfaces().length>0){beanMap.put(clazz.getInterfaces()[0],instance);}else {beanMap.put(clazz,instance);}}}}}}}}
}
实现service和dao的实现类
package com.dao;import com.anno.Bean;@Bean
public class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("UserDaoImpl add");}}
package com.service;import com.anno.Bean;
import com.anno.Di;
import com.dao.UserDao;@Bean
public class UserServiceImpl implements UserService{@Diprivate UserDao userDao;@Overridepublic void add() {System.out.println("userservice add");}
}
测试:
package com;import com.bean.AnnotationApplicationContext;
import com.bean.ApplicationContext;
import com.service.UserService;import java.net.URL;
import java.util.Enumeration;public class Main {public static void main(String[] args) {ApplicationContext context=new AnnotationApplicationContext("com");UserService userService=(UserService)context.getBean(UserService.class);System.out.println(userService);userService.add();}
}
修改AnnotationApplicationContext代码,增加属性注入 。getbean里增加loadDi()
private void loadDi(){//遍历beanMapfor (Map.Entry<Class,Object> entry:beanMap.entrySet()){//获取bean的所有属性Object obj=entry.getValue();//获取对象ClassClass<?> clazz=obj.getClass();//获取对象的所有属性Field[] fields=clazz.getDeclaredFields();//遍历属性for (Field field:fields){//判断属性是否有Di注解Di annotation=field.getAnnotation(Di.class);if(annotation!=null){field.setAccessible(true);//如果有Di注解,就注入属性try {field.set(obj,beanMap.get(field.getType()));} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}}
测试
4、AOP
一、代理模式
核心代码与日志代码在一起不合理,不利于维护。所以使用代理模式
1、静态代理
再写一个接口的实现类,定义一个核心实现类的对象作为属性,然后同样的方法中去调用,前后增加自己的代码。
但是代码写死了没有具备灵活性。
2、动态代理
public Object getProxy(){//得到类加载器ClassLoader classloader=target.getClasss().getClassLoader();//目标对象实现接口的数组形式Class<?> interfaces=target.getClass().getInterfaces();//设置实现目标方法的过程InvocationHandler invocationhandler= new InvocationHandler(){public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
//代理对象、需要重写的方法,方法里面的参数System.out.println("动态代理日志1");Object result=method.invoke(target,args);System.out.println("动态代理日志2");return result;}};return Proxy.newProxyInstance(classloader,interfaces,invocationhandler);}
二、概念
面向切面编程,通过预编译和运行期间动态代理方式实现,在不改变原代码的情况下,给程序动态统一添加额外功能的一种技术。
- 横切关注点:各个模块解决同一个问题。如事务、日志都属于横切关注点
- 通知:想要增强的功能,比如事务、日志,所实现的方法叫通知方法
- 切面:封装通知方法的类。
- 目标:目标对象
- 代理:代理对象
- 连接点:spring允许你使用通知的位置
- 切入点:定位连接点的方式
三、注解实现AOP
切入点表达式:
JoinPoint获得切入点信息
切面的优先级用@Order实现
四、xml实现AOP
<aop:config>
<aop:aspect ref="logAspect">
<aop:pointcut id="pointcut" expression="execution(* com.*((..))"/>
<aop:before method:"beforemethod" pointcut-ref="pointcut"></aop:before>
</aop:aspect>
</aop:config>