java自定义类加载器,Springboot热部署,aop拦截,文件监听

java自定义类加载器、Springboot热部署原理
1、自定义类加载器实现热加载
2、aop拦截
3、文件监听
4、异常:bean方法无法调用,报空异常

1、配置Configuration

1.1 Configuration 类代码

@Configuration
@ComponentScan("com.xxx.common.extend.controller")
@EnableAspectJAutoProxy
//@EnableWebMvc
public class CommonControlConfig{// implements WebMvcConfigurer {

    @Bean
    public void testTestRpcTestImpl(){
        try{
            System.out.println("--------------Application-------------start----------------------");
            Application.run();
            System.out.println("--------------Application--------------end-----------------------");
        }catch (Throwable e){
            e.printStackTrace();
        }


    }
//    @Bean
//    public HandlerInterceptor getMyInterceptor(){
//        return new UrlInterceptor();
//    }
//
//    @Override
//    public void addInterceptors(InterceptorRegistry registry) {
//        // addPathPatterns 用于添加拦截规则, 这里假设拦截 /url 后面的全部链接
//        // excludePathPatterns 用户排除拦截
//        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");
//        //WebMvcConfigurer.super.addInterceptors(registry);
//    }



}

1.2 spring.factories配置文件

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xxxxxxxxxx

2、代码实现

2.1 引入maven

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.2 aop拦截

1、aop拦截,定义拦截的包

@Aspect
@Component
public class AuthorityAspect {


    /**
     * 用于拦截类的切点
     * 此处注意execution(*后面要有空格否则会报错type name pattern expected
     */
    @Pointcut("execution (* com.xxx.xxx.xxx.xxx..*.*(..))")
    //第一个..指api包下的所有类包括子包下的类启用拦截,如果改成.则不包括子包下的类;第二个..类中的所有方法启用拦截
    private void cutTokens(){

    }



    @Around("cutTokens()")
    public Object doToken(ProceedingJoinPoint joinPoint)   {
        Object out= null;
        try{

            String className=joinPoint.getSignature().getDeclaringTypeName();
            String methodName=joinPoint.getSignature().getName();
            Object[] args =  joinPoint.getArgs() ;
            //System.out.println("拦截了");
            if(className.contains("ServiceImpl")){

                Object[] joinPointArgs = joinPoint.getArgs();
                if(joinPointArgs==null){
                    throw new RuntimeException("获取参数异常");
                }
                //int userId = (int)joinPointArgs[0];
                //log.info("解析userId:"+userId);
                //构造器会重新加载rootPath+packagePath下的class文件
                //MyClassLoader myClassLoader = new MyClassLoader(Application.rootPath,Application.rootPath+Application.packagePath );
                out=run(  Application.myClassLoader, className, methodName, args);
            }else  if(className.contains("controller")){
                System.out.println("controller拦截了");
                Object[] joinPointArgs = joinPoint.getArgs();
                if(joinPointArgs==null){
                    throw new RuntimeException("获取参数异常");
                }
                //int userId = (int)joinPointArgs[0];
                //log.info("解析userId:"+userId);
                //构造器会重新加载rootPath+packagePath下的class文件
                //MyClassLoader myClassLoader = new MyClassLoader(Application.rootPath,Application.rootPath+Application.packagePath );
                out=run(  Application.myClassLoader, className, methodName, args);
            }else{
                return joinPoint.proceed();
            }


        }catch (Throwable e){
            e.printStackTrace();
        }
        return  out;


    }


    public   Object run(MyClassLoader myClassLoader, String className, String methodName, Object[] args) throws Exception {
        Class[] classs =new Class[args.length];
        for(int i=0;i<args.length;i++){
            classs[i]=args[i].getClass();
        }
        System.out.println("className:"+className+",methodName:"+methodName);
        Class<?> aClass = myClassLoader.loadClass(className);
        //Object out= aClass.getMethod(methodName,classs).invoke(aClass.newInstance(),args);
        Object out= invokMethod( aClass,  methodName,  classs, args );
        if(out==null){
            out= aClass.getMethod(methodName,classs).invoke(aClass.newInstance(),args);
        }
        return out;
        //不可以直接调用start,只能用自己的myClassLoader反射调用start,全盘委托机制
        //start();
    }


    public Object invokMethod(Class<?> aClass,String methodName,Class[] classs,Object[] args ){
        Object out=null;
        try {
            Object bean = SpringBeanUtils.getBean(aClass);
            //ApplicationContext applicationContext=SpringBeanUtils.getApplicationContext();
            //SpringBeanUtils.getApplicationContext().getAutowireCapableBeanFactory().autowireBean(bean);
            //applicationContext.getAutowireCapableBeanFactory().autowireBean(applicationContext.getBean(aClass));
            Method method =aClass.getDeclaredMethod(methodName,classs);
            // 设置访问权限
            method.setAccessible(true);
            out= method.invoke(bean,args);

        }catch (Throwable e){
            e.printStackTrace();
        }
        return  out;
    }

2.3 自定义类加载器

public class MyClassLoader extends ClassLoader {
    public String rootPath;


    private Map<String, byte[]> clazzs = new HashMap<>();

    public MyClassLoader(String rootPath, String... classPaths) throws Exception {
        this.rootPath = rootPath;
        //this.clazzs = new HashMap<>();

        for (String classPath : classPaths) {
            scanClassPath(new File(classPath));
        }
    }

    private void scanClassPath(File file) {
        try {
            if (file.isDirectory()) {
                for (File listFile : file.listFiles()) {
                    scanClassPath(listFile);
                }
            } else {
                String fileName = file.getName();
                //String filePath = file.getPath();
                //String suffix = fileName.substring(fileName.lastIndexOf("."));
                if (fileName.endsWith("jar")) {

                    JarFile jarFile = new JarFile(file);
                    Enumeration<JarEntry> entries = jarFile.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry jarEntry = entries.nextElement();
                        String name = jarEntry.getName();
                        if (name.endsWith("class")) {
                            try (InputStream inputStream = jarFile.getInputStream(jarEntry)) {
                                int pos = 0;
                                int len;
                                byte[] buff = new byte[inputStream.available()];
                                while ((len = inputStream.read(buff, pos, buff.length - pos)) > 0) {
                                    pos += len;
                                }
                                String className = jarEntry.getName().replace("/", ".")
                                        .replace(".class", "");
                                clazzs.put(className, buff);
                            } catch (Throwable e) {
                                System.out.println("scanClassPath获取失败:" + fileName);
                            }

                        }
                    }

                    //System.out.println("scanClassPath加载:" + fileName);
                }


            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }


    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            Class<?> loadClass = findLoadedClass(name);
            if (loadClass == null) {
                byte[] bytes = clazzs.get(name);
                try {

                    if (bytes == null) {

                        loadClass = super.loadClass(name);
                        System.out.println("super加载:" + name);
                    } else {

                        loadClass = defineClass(name, bytes, 0, bytes.length);
                        System.out.println("自定义加载器加载:" + name);
                    }
                } catch (Throwable e) {
                    System.out.println("加载失败:-------" + name);
                    //e.printStackTrace();
                }
                try {
                    if (loadClass == null) {

                        loadClass = findClass(name);
                        System.out.println("findClass加载:" + name);
                    }
                } catch (Throwable e) {
                    System.out.println("findClass加载失败:-------" + name);
                    //e.printStackTrace();
                }
                if (loadClass == null) {
                    try {


                        loadClass = Thread.currentThread().getContextClassLoader().loadClass(name);
                        System.out.println("ContextClassLoader加载 :-------" + name);
                    } catch (Throwable e) {
                        System.out.println("ContextClassLoader加载失败:-------" + name);
                    }
                }

            }
            return loadClass;
        }
    }


}

2.4 运行方法

1、run() 程序启动时启动监听,startFileListener方法启动监听

@Data
@AllArgsConstructor(access = AccessLevel.PUBLIC)
public class Application {
    public static String rootPath;
    public static String packagePath  = "#################";//jar包对应路径


    public static MyClassLoader myClassLoader;


    public static void run() {
        try{
            String rootPath = MyClassLoader.class.getResource("/").getPath().replaceAll("%20", " ");
            rootPath = new File(rootPath).getPath();
            //处理路径
            rootPath = CommonFun.pathHandle(rootPath);
            Application.rootPath = rootPath;
            MyClassLoader myClassLoader2=new MyClassLoader(rootPath,rootPath+packagePath);
            startFileListener(rootPath+ packagePath);
            myClassLoader=myClassLoader2;
        }catch (Throwable e){
            e.printStackTrace();
        }
    }


    //监听文件改动,一旦文件发生改动,则回调FileListener的onFileChange
    public static void startFileListener(String rootPath) {
        try {
            //String listenerPath =rootPath+"/apps/tbxm/tbxmmes";
            // System.out.println("listenerPath:"+listenerPath);

            FileAlterationObserver fileAlterationObserver = new FileAlterationObserver(rootPath);
            fileAlterationObserver.addListener(new FileListener());
            FileAlterationMonitor fileAlterationMonitor = new FileAlterationMonitor(1000);
            fileAlterationMonitor.addObserver(fileAlterationObserver);
            fileAlterationMonitor.start();
        } catch (Throwable e) {
            System.out.println("文件监听启动失败");
        }

    }

}

2.5 文件监听

1、文件监听方法 ,onFileChange 文件修改后,监听jar是否变化

public class FileListener extends FileAlterationListenerAdaptor {


    @Override
    public void onFileChange(File file) {
        if (file.getName().endsWith("jar")){
            System.out.println("文件监听,文件发生变化----"+file.getName());
            try {

                MyClassLoader myClassLoader =new MyClassLoader(Application.rootPath,file.getPath());
                Application.myClassLoader=myClassLoader;

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

}

2.6 方法类

1、用到的公共方法 pathHandle 处理 路径

public class CommonFun {
    public static String pathHandle(String instr) {
        String out = instr;
        if (instr.contains("runt")) {
            int index = instr.lastIndexOf("run");
            out = instr.substring(0, index - 1);
        }

        return out;
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,关于SpringBoot定义注解AOP的问题,我可以为您提供一些基本的介绍和示例代码。 首先,AOP(Aspect-Oriented Programming)是一种编程范式,它可以在不修改业务逻辑代码的情况下,对应用程序进行横切关注点的切面处理。而Spring AOP作为Spring框架的一部分,提供了一种基于代理模式的AOP实现。 在使用Spring AOP的过程中,自定义注解可以作为切点表达式的一部分,通过对注解的解析,实现对被注解的方法或类的切面处理。下面是一个简单的示例代码,演示如何通过自定义注解实现对方法的AOP处理: 首先,定义一个自定义注解: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyAnnotation { String value() default ""; } ``` 然后,在需要被拦截的方法上添该注解: ```java @Service public class MyService { @MyAnnotation("myAnnotation") public void doSomething() { System.out.println("do something..."); } } ``` 接下来,使用AspectJ的@Aspect注解定义一个切面类,并在该类中定义一个切点,用于匹配被@MyAnnotation注解的方法: ```java @Aspect @Component public class MyAspect { @Pointcut("@annotation(com.example.demo.annotation.MyAnnotation)") public void myAnnotationPointcut() {} @Before("myAnnotationPointcut()") public void beforeMyAnnotation() { System.out.println("before myAnnotation..."); } } ``` 最后,启动SpringBoot应用程序,调用MyService的doSomething方法,就可以看到输出结果: ```java before myAnnotation... do something... ``` 以上就是一个简单的SpringBoot定义注解AOP的示例。通过使用自定义注解,可以更方便地实现对应用程序的切面处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值