Java 开发,如果要运行程序,首先就是编译源码,其次是程序包启动加载类,最后程序运行。
众所周知 Java 是基于 Jvm 虚拟机运行的,那么程序启动时怎么区分开发人员定义的类
和 Jvm 提供的类呢;如果不区分,万一开发人员定义了一个 Stirng 类,很有可能导致 Jvm 瘫痪...
为了避免上述情况, 类加载时采用了双亲委派模型,即向上委托,向下查找:
某些官方类由顶级类加载器加载,这样开发人员引入官方类时,向上委托加载;
如果引入自定义类,上层加载器查不到,则向下查找,最终有应用层类加载器,加载自定义类
同时类加载时,已加载过的类会直接返回,不会重复加载
所以,如果要动态编译源码,增加新的类,新的处理逻辑比较容易;想要修改已有的类,并让程序引用到新的类,比较困难,因为已加载的类如果不卸载,是不会重新加载同名类的;同时,双亲委派模型是可以打破的,比较典型的实现有 Tomcat
关于 Jvm 类加载,双亲委派,和打破双亲委派的更多细节,大家可以自行学习
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>DynamicDemoartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>19maven.compiler.source>
<maven.compiler.target>19maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>2.7.2version>
dependency>
dependencies>
project>
SpringBoot 最重要的实现就是 IOC 和 AOP ,IOC 使我们可以通过注解方便的引用单例类对象
如果要基于 SpringBoot 应用实现源码动态编译,我们可以定义一个 SpringBoot 的 Bean 工具类,用于移除和注册 Bean 对象,同时如果可能设计修改的类,需要用抽象类或接口定义,此时修改实现类,则相当于修改方法;这样,基于上一篇的自定义类加载器加载新的类,可以避过同名类无法重复加载的问题
package org.example.demo.util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author
* @date 2023-02-16 20:20
* @since 1.8
*/
@Component
public class SpringBeanUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private static ConfigurableApplicationContext context ;
/**
* 获取 Bean 工厂
*/
private static DefaultListableBeanFactory beanFactory ;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringBeanUtil.applicationContext = applicationContext;
SpringBeanUtil.context= (ConfigurableApplicationContext) applicationContext;
SpringBeanUtil.beanFactory= (DefaultListableBeanFactory) context.getBeanFactory();
}
/**
* 注册 Bean
* @param clazz
*/
public static void register(String className,Class clazz){
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
BeanDefinition definition = builder.getBeanDefinition();
//为 definition 设置额外属性
definition.setScope("singleton");
//定义 beanName 可以通过类注解获取 clazz.getAnnotations() 此处直接取类名
String beanName = StringUtils.uncapitalize(className.substring(className.lastIndexOf(".") + 1));
//注册
beanFactory.registerBeanDefinition(beanName,definition);
}
/**
* 卸载 Bean
* @param className
*/
public static void unregister(String className){
beanFactory.removeBeanDefinition(className);
}
/**
* 获取所有 Bean
* @return
*/
public static List<String> getBeans(String beanName){
String[] names = applicationContext.getBeanDefinitionNames();
List<String> beans = new ArrayList<>(names.length);
for (String name:names){
beans.add(applicationContext.getBean(name).getClass().getName());
}
return beans;
}
/**
* bean 是否存在
* @param name
* @return
*/
public static boolean isBeanExist(String name){
return applicationContext.containsBean(name);
}
/**
* 通过名称获取 Bean
* @param name
* @return
* @param
* @throws BeansException
*/
public static <T> T getBean(String name) throws BeansException{
return (T) applicationContext.getBean(name);
}
/**
* 通过类型获取 Bean
* @param clazz
* @return
* @param
* @throws BeansException
*/
public static <T> T getBean(Class<?> clazz) throws BeansException{
return (T) applicationContext.getBean(clazz);
}
/**
* 获取指定类型的 Bean 的名称
* @param className
* @return
* @throws BeansException
*/
public static List<String> getBeanName(String className) throws BeansException, ClassNotFoundException {
Class<?> clazz = Class.forName(className);
return Arrays.asList(applicationContext.getBeanNamesForType(clazz));
}
}
定义一个类编译管理器,用于获取源码和编译
package org.example.demo.util;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author
* @date 2023-02-16 20:24
* @since 1.8
*/
@Component
public class SourceAndClassManage {
private static Map<String,String> classNameMap = new HashMap<>(2);
private static Map<String,Class> classObjMap = new HashMap<>(2);
private static CustomClassCompiler compiler = null;
/**
* 类名和类路径(可从表、配置文件获取)
*/
static {
classNameMap.put("userAuth","org.example.demo.auth.UserAuth");
classNameMap.put("userServiceImpl","com.lenovo.demo.service.impl.UserServiceImpl");
compiler = CustomClassCompiler.newInstance(null);
}
/**
* 更新源码并编译
* @param className
*/
public void updateAndCompile(String className){
/**
* 读文件、读表、读缓存
*/
String sourceCode = "package org.example.demo.auth;\n" +
"import org.springframework.stereotype.Component;\n" +
"import org.example.demo.auth.UserAuthAbstract;\n" +
"@Component(\"userAuth\")\n" +
"public class UserAuth extends UserAuthAbstract{\n" +
" @Override\n" +
" public void auth() {\n" +
" System.out.println(\"用户第三方认证 V2.\");\n" +
" }\n" +
"}";
className = classNameMap.get(className);
compiler.addSource(className, sourceCode);
compiler.compile(className);
}
/**
* 获取编译后的类
* @param className
* @return
*/
public Class<?> getClassByName(String className){
return compiler.getClassByName(classNameMap.get(className));
}
}
控制层
package org.example.demo.controller;
import org.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author
* @date 2023-02-16 20:39
* @since 1.8
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
/**
* 登陆
*/
@GetMapping("/login")
public void login(){
userService.login();
}
}
接口层
package org.example.demo.service;
/**
* @author
* @date 2023-02-16 20:40
* @since 1.8
*/
public interface UserService {
/**
* 登陆
*/
void login();
}
实现层
package org.example.demo.service.impl;
import org.example.demo.auth.UserAuthAbstract;
import org.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author
* @date 2023-02-16 20:40
* @since 1.8
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserAuthAbstract userAuth;
@Override
public void login() {
userAuth.auth();
}
}
用户登陆认证抽象类
package org.example.demo.auth;
/**
* @author
* @date 2023-02-16 20:42
* @since 1.8
*/
public abstract class UserAuthAbstract {
/**
* 用户认证
*/
public abstract void auth();
}
用户认证默认实现
package org.example.demo.auth;
import org.springframework.stereotype.Component;
/**
* @author
* @date 2023-02-16 20:43
* @since 1.8
*/
@Component("userAuth")
public class UserAuthV1 extends UserAuthAbstract{
@Override
public void auth() {
System.out.println("用户第三方认证 V1.");
}
}
http://127.0.0.1:8088/user/login
http://127.0.0.1:8088/bean/update?className=userAuth
http://127.0.0.1:8088/bean/register?beanName=userAuth
新的认证源码直接写在了上面的类中
可以看到,再次登陆时,认证方式已经变为 V2