4、原理-手写IOC
4.1 Java反射机制
反射是指 对于任意一个类 能知道这个类的属性和方法 对于任意一个对象 都能调用它任意的方法和属性 动态获取信息以及动态调用对象方法的功能成为反射
要想解剖一个类 首先要获取该类的Class对象 剖析一个类用反射解决问题就是使用相关的API
public class TestCar {
//1、获取Class对象
@Test
public void test01() throws Exception {
//1、类名.class
Class<Car> clazz1 = Car.class;
//2、对象.getClass()
Class clazz2 = new Car().getClass();
//3、Class.forName("全路径")
Class clszz3 = Class.forName("com.szy.reflect.Car");
//实例化
Car car = (Car) clszz3.getDeclaredConstructor().newInstance();
System.out.println(car);
}
//2、用反射获取构造方法
@Test
public void test02()throws Exception{
Class<Car> clazz = Car.class;
//获取所有构造
//getConstructors()获取所有public构造方法
//getDeclaredConstructors()获取所有构造方法
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("构造方法名称:"+constructor.getName()+"参数个数:"+constructor.getParameterCount());
}
//指定有参数的构造创建对象
//1、构造public
Constructor<Car> c1 = clazz.getConstructor(String.class, int.class, String.class);
Car car1 = c1.newInstance("夏利", 10, "红色");
System.out.println(car1);
}
//3、用反射获取属性
@Test
public void test03()throws Exception{
Class<Car> clazz = Car.class;
Car car = clazz.getDeclaredConstructor().newInstance();
//Field[] fields = clazz.getFields();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
if (declaredField.getName().equals("name")){
//设置运行访问
declaredField.setAccessible(true);
declaredField.set(car,"五菱宏光");
}
System.out.println(declaredField.getName());
System.out.println(car);
}
}
//4、用反射方法
@Test
public void test04()throws Exception{
Car car = new Car("奔驰", 10, "黑色");
Class clazz = car.getClass();
//1、public方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
//执行方法
if (method.getName().equals("toString")){
String invoke = (String) method.invoke(car);
System.out.println("toString方法执行了"+invoke);
}
}
//2、private方法
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
//执行run方法
if (declaredMethod.getName().equals("run")){
declaredMethod.setAccessible(true);
declaredMethod.invoke(car);
}
}
}
}
4.2实现Spring的IOC
自己实现IOC和DI
实现过程:
1、创建子模块
2、创建测试类 service dao
3、创建两个注解(自己写)@Bean @Di
4、自己常见一个bean容器的接口ApplicationContext 定义方法 返回对象
5、实现bean容器的接口
(1) 返回对象
(2) 根据包规则加载bean
扫描com.szy包以及其子包下的所有类 开类上面是否有我们创建的注解@Bean 如果有把这个类通过反射实例化
4.2.1 搭建子模块
4.2.1.1 自定义注解
//Bean注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
//Di注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
4.2.1.2 DAO层
//UserDao.interface
public interface UserDao {
public void run();
}
//UserDaoImpl.class
@Bean
public class UserDaoImpl implements UserDao {
@Override
public void run() {
System.out.println("dao........");
}
}
4.2.1.3 Service层
public interface UserService {
public void add();
}
@Bean
public class UserServiceImpl implements UserService {
@Di
private UserDao userDao;
@Override
public void add() {
System.out.println("service....");
userDao.run();
}
}
4.2.2 手写注入类
//接口
public interface ApplicationContext {
Object getBean(Class clazz);
}
//实现类
public class AnnotationApplicationContext implements ApplicationContext{
//创建map集合用于放入bean对象
private Map<Class,Object> beanFactory = new HashMap();
private static String rootPath;
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
//设置扫描规则
//当前包及其子类 哪个类有@Bean注解 把这个类通过反射实例化
//创建有参数构造 传入
public AnnotationApplicationContext(String basePackage) {
try {
//1、把.替换成斜杠
String packagePath = basePackage.replaceAll("\\.", "\\\\");
//2、获取包的绝对路径
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
while (urls.hasMoreElements()){
URL url = urls.nextElement();
String filepath = URLDecoder.decode(url.getFile(), "utf8");
//获取包前面路径部分
rootPath = filepath.substring(0,filepath.length()-packagePath.length());
//包扫描
loadBean(new File(filepath));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
loadDi();
}
//包扫描的过程 让bean实例化
private void loadBean(File file) throws Exception{
//1、判断当前是否为文件夹
if (file.isDirectory()){
//2、获取下文件夹所有的内容
File[] childrenFiles = file.listFiles();
//3、判断文件夹里面为空 直接返回
if (childrenFiles==null || childrenFiles.length==0){
return;
}
//4、如果文件夹不为空 遍历文件夹所有内容
for (File child : childrenFiles) {
//4.1 遍历得到每一个File对象 继续判断 如果还是一个文件夹继续递归遍历
if (child.isDirectory()){
loadBean(child);
}else {
//4.2 遍历得到不是一个文件夹是文件
//4.3 得到包路径+类名称部分-字符串提取过程
String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
//4.4 判断当前文件类型是否为.class
if (pathWithClass.contains(".class")){
//4.5 如果是.class 把路径\替换成. 把.class去掉
String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class","");
//4.6 判断类上面是否有注解@Bean 如果有就实例化
//4.6.1获取类的Class
Class clazz = Class.forName(allName);
//4.6.2判断否为接口
if (!clazz.isInterface()){
//4.6.3 判断类上是否有注解
Bean annotation = (Bean) clazz.getAnnotation(Bean.class);
if (annotation!=null){
//4.6.4 实例化
Object instance = clazz.getConstructor().newInstance();
//4.7 把对象实例化后放入map集合中去
//4.7.1 该类有接口让接口作为key 没有接口自己的class就是key
if (clazz.getInterfaces().length>0){
beanFactory.put(clazz.getInterfaces()[0],instance);
}else {
beanFactory.put(clazz,instance);
}
}
}
}
}
}
}
}
//属性注入
public void loadDi(){
//实例化对象在beanFactory集合里面
//1、遍历beanFactory中的map集合
Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
for (Map.Entry<Class, Object> entry : entries) {
//2、获取map集合中的每个对象value 每个对象属性获取到
Object obj = entry.getValue();
Class<?> clazz = obj.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
//3、遍历得到每个对象的属性数组 得到每个属性
for (Field declaredField : declaredFields) {
//4、判断属性上是否有注解@Di
Di annotation = declaredField.getAnnotation(Di.class);
if (annotation!=null){
// 如果是私有属性 设置可以设置值
declaredField.setAccessible(true);
//5、如果有@Di注解 把对象进行设置(注入)
try {
declaredField.set(obj,beanFactory.get(declaredField.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
//测试类
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new AnnotationApplicationContext("com.szy");
UserService userService = (UserService) context.getBean(UserService.class);
System.out.println(userService);
userService.add();
}
}
结果
com.szy.service.Impl.UserServiceImpl@77459877
service....
dao........
Process finished with exit code 0