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对象的三种方式
@Test
public void test1() throws Exception {
//方式一:调用运行时类的属性.class
Class 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);
}
}
获取构造方法,并设置对象参数
@Test
public 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);
}
获取属性并设置
@Test
public 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);
}
}
获取方法得到返回值
@Test
public 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;
@Override
public 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());
//加载bean
loadBean(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文件,就加载bean
if(pathwithclass.contains(".class"))
{
//把路径转化为包名
String allname=pathwithclass.replaceAll("\\\\",".").replace(".class","");
Class<?> clazz=Class.forName(allname);
//不是接口
if(!clazz.isInterface())
{
Bean annotation=clazz.getAnnotation(Bean.class);
//是bean
if(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 {
@Override
public 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{
@Di
private UserDao userDao;
@Override
public 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()
{
//遍历beanMap
for (Map.Entry<Class,Object> entry:beanMap.entrySet())
{
//获取bean的所有属性
Object obj=entry.getValue();
//获取对象Class
Class<?> 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>