javassist库小实例的简单介绍

发布时间:2022-11-20

本文目录一览:

  1. 使用JAVASSIST怎样向Class文件中加入import语句?
  2. struts2.2 jar包里为什么没有javassist.jar包
  3. springmnv框架怎么搭建
  4. 用Javassist修改方法的几个问题
  5. 用JAVASSIST如何得到一个类中所有的内部类(inner class)?
  6. javassist-3.11.0.GA.jar 起什么作用

使用JAVASSIST怎样向Class文件中加入import语句?

在ECLIPSE下打开我这个JAVASSIST工程的属性对话框,选择Java Build Path - Library,把将要用到的库导入进去,JAVASSIST就能找到了

struts2.2 jar包里为什么没有javassist.jar包

这个包是 struts2.2.1开始才依赖的,可能开发组没来得及加进下载包里吧!之前版本的struts是不需要这个包的,不知道你用不用maven,用的话只要写依赖就行了,maven会自动去它的仓库下载的,不过还是给仓库的地址你好了,呵呵 需要哪个版本就选哪个吧

springmnv框架怎么搭建

一、Spring MNV环境搭建:

1. jar包引入

  • Spring 2.5.6:spring.jar、spring-webmvc.jar、commons-logging.jar、cglib-nodep-2.1_3.jar
  • Hibernate 3.6.8:hibernate3.jar、hibernate-jpa-2.0-api-1.0.1.Final.jar、antlr-2.7.6.jar、commons-collections-3.1、dom4j-1.6.1.jar、javassist-3.12.0.GA.jar、jta-1.1.jar、slf4j-api-1.6.1.jar、slf4j-nop-1.6.4.jar、相应数据库的驱动jar包

2. web.xml配置(部分)

<!-- Spring MVC配置 -->
<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[servlet-name]-servlet.xml,如spring-servlet.xml -->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- Spring配置 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 指定Spring Bean的配置文件所在目录。默认配置在WEB-INF目录下 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:config/applicationContext.xml</param-value>
</context-param>

3. spring-servlet.xml配置

spring-servlet这个名字是因为上面web.xml中servlet-name标签配的值为spring(servlet-name>spring),再加上“-servlet”后缀而形成的spring-servlet.xml文件名,如果改为springMVC,对应的文件名则为springMVC-servlet.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 启用spring mvc 注解 -->
    <context:annotation-config />
    <!-- 设置使用注解的类所在的jar包 -->
    <context:component-scan base-package="controller" />
    <!-- 完成请求和注解POJO的映射 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
    <!-- 对转向页面的路径解析。prefix:前缀, suffix:后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/jsp/" p:suffix=".jsp" />
</beans>

4. applicationContext.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 采用hibernate.cfg.xml方式配置数据源 -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation">
            <value>classpath:config/hibernate.cfg.xml</value>
        </property>
    </bean>
    <!-- 将事务与Hibernate关联 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref local="sessionFactory"/>
        </property>
    </bean>
    <!-- 事务(注解) -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    <!-- 测试Service -->
    <bean id="loginService" class="service.LoginService"/>
    <!-- 测试Dao -->
    <bean id="hibernateDao" class="dao.HibernateDao">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
</beans>

二、详解

Spring MVC与Struts从原理上很相似(都是基于MVC架构),都有一个控制页面请求的Servlet,处理完后跳转页面。看如下代码(注解):

package controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import entity.User;
@Controller // 类似Struts的Action
public class TestController {
    @RequestMapping("test/login.do") // 请求url地址映射,类似Struts的action-mapping
    public String testLogin(@RequestParam(value="username") String username, String password, HttpServletRequest request) {
        // @RequestParam是指请求url地址映射中必须含有的参数(除非属性required=false)
        // @RequestParam可简写为:@RequestParam("username")
        if (!"admin".equals(username) || !"admin".equals(password)) {
            return "loginError"; // 跳转页面路径(默认为转发),该路径不需要包含spring-servlet配置文件中配置的前缀和后缀
        }
        return "loginSuccess";
    }
    @RequestMapping("/test/login2.do")
    public ModelAndView testLogin2(String username, String password, int age){
        // request和response不必非要出现在方法中,如果用不上的话可以去掉
        // 参数的名称是与页面控件的name相匹配,参数类型会自动被转换
        if (!"admin".equals(username) || !"admin".equals(password) || age < 5) {
            return new ModelAndView("loginError"); // 手动实例化ModelAndView完成跳转页面(转发),效果等同于上面的方法返回字符串
        }
        return new ModelAndView(new RedirectView("../index.jsp")); // 采用重定向方式跳转页面
        // 重定向还有一种简单写法
        // return new ModelAndView("redirect:../index.jsp");
    }
    @RequestMapping("/test/login3.do")
    public ModelAndView testLogin3(User user) {
        // 同样支持参数为表单对象,类似于Struts的ActionForm,User不需要任何配置,直接写即可
        String username = user.getUsername();
        String password = user.getPassword();
        int age = user.getAge();
        if (!"admin".equals(username) || !"admin".equals(password) || age < 5) {
            return new ModelAndView("loginError");
        }
        return new ModelAndView("loginSuccess");
    }
    @Resource(name = "loginService") // 获取applicationContext.xml中bean的id为loginService的,并注入
    private LoginService loginService; // 等价于spring传统注入方式写get和set方法,这样的好处是简洁工整,省去了不必要得代码
    @RequestMapping("/test/login4.do")
    public String testLogin4(User user) {
        if (loginService.login(user) == false) {
            return "loginError";
        }
        return "loginSuccess";
    }
}

以上4个方法示例,是一个Controller里含有不同的请求url,也可以采用一个url访问,通过url参数来区分访问不同的方法,代码如下:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/test2/login.do") // 指定唯一一个*.do请求关联到该Controller
public class TestController2 {
    @RequestMapping
    public String testLogin(String username, String password, int age) {
        // 如果不加任何参数,则在请求/test2/login.do时,便默认执行该方法
        if (!"admin".equals(username) || !"admin".equals(password) || age < 5) {
            return "loginError";
        }
        return "loginSuccess";
    }
    @RequestMapping(params = "method=1", method=RequestMethod.POST)
    public String testLogin2(String username, String password) {
        // 依据params的参数method的值来区分不同的调用方法
        // 可以指定页面请求方式的类型,默认为get请求
        if (!"admin".equals(username) || !"admin".equals(password)) {
            return "loginError";
        }
        return "loginSuccess";
    }
    @RequestMapping(params = "method=2")
    public String testLogin3(String username, String password, int age) {
        if (!"admin".equals(username) || !"admin".equals(password) || age < 5) {
            return "loginError";
        }
        return "loginSuccess";
    }
}

其实@RequestMapping在Class上,可看做是父Request请求url,而@RequestMapping在方法上的可看做是子Request请求url,父子请求url最终会拼起来与页面请求url进行匹配,因此@RequestMapping也可以这么写:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/test3/*") // 父request请求url
public class TestController3 {
    @RequestMapping("login.do") // 子request请求url,拼接后等价于/test3/login.do
    public String testLogin(String username, String password, int age) {
        if (!"admin".equals(username) || !"admin".equals(password) || age < 5) {
            return "loginError";
        }
        return "loginSuccess";
    }
}

用Javassist修改方法的几个问题

可以通过一种手段来在程序内部修改授权部分的实现,使真实的授权部分隐藏在其它代码部分,而可视的授权代码并不参与实际的授权,这样的话,对于破解者来说,修改表向的代码实现并不能真正修改代码实现,因为真实的实现已经通过其它代码将原始实现替换掉了。 即在调用授权代码之前将授权原代码进行修改,然后调用授权代码时即调用已经修改后的授权代码,而真实的授权代码是查看不了的(通过某种方式注入),这样即达到一种授权方式的隐藏。 可以通过javassist来修改java类的一个方法,来修改一个方法的真实实现。修改的方法可以是动态方法,也可以是静态方法。修改的前提即是当前修改的类还没有被当前jvm加载,如果当前的类已经被加载,则不能修改。

ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("com.develop.Txt");
CtMethod ctMethod = ctClass.getDeclaredMethod("i");
ctMethod.setBody("{try{Integer i = null;"
        + "int y = i.intValue();System.out.println(\"this is a new method\");");
ctClass.toClass();

上面的方法即是修改一个方法的实现,当调用ctClass.toClass()时,当前类即会被当前的classLoader加载,并实例化类。 需要注意的是,在调用ctClass.toClass()时,会加载此类,如果此类在之前已经被加载过,则会报一个duplicate load的错误,表示不能重复加载一个类。所以,修改方法的实现必须在修改的类加载之前进行。 即使不调用toClass,那么如果当前修改的类已经加载,那么修改方法实现,同样不起作用,即修改一个已经加载的类(不论是修改静态方法,还是修改动态方法)是没有任何效果的。修改之前必须在类加载之前进行。 当然,使用aspectj也可以同样达到修改的效果,不过修改指定的类,则需要为修改这个类添加一个aspect,然后将这个aspect加入配置文件中以使其生效,比起javassist来说,修改一个类还是使用javassist相对简单一点。

用JAVASSIST如何得到一个类中所有的内部类(inner class)?

内部类:内部类也就是定义在类内部的类。

内部类的分类:

  • 成员内部类
  • 局部内部类
  • 静态内部类
  • 匿名内部类

成员内部类

四个访问权限修饰符都可以修饰成员内部类。 内部类和外部类在编译时时不同的两个类,内部类对外部类没有任何依赖。 内部类是一种编译时语法,在编译时生成的各自的字节码文件,内部类和外部类没有关系。 内部类中可以访问外部类的私有成员。 作为外部类的一个成员存在,与外部类的属性、方法并列。 内部类和外部类的实例变量可以共存。 在内部类中访问实例变量:this.属性 在内部类访问外部类的实例变量:外部类名.this.属性 在外部类的外部访问内部类,使用out.inner 成员内部类的特点:

  1. 内部类作为外部类的成员,可以访问外部类的私有成员或属性。(即使将外部类声明为private,但是对于处于其内部的内部类还是可见的。)
  2. 用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。
  3. 成员内部类不能含有静态成员。 注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。 对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.classouter$inner.class两类。 建立内部类对象时应注意: 在外部类的内部可以直接使用inner s = new inner();(因为外部类知道inner是哪个类,所以可以生成对象。) 而在外部类的外部,要生成(new)一个内部类对象,需要首先建立一个外部类对象(外部类可用),然后在生成一个内部类对象。内部类的类名是外部类类名.内部类类名。
Outer o = new Outer();
Outer.Inner in = o.new Inner();

静态内部类

静态内部类定义在类中,任何方法外,用static class定义。 静态内部类只能访问外部类的静态成员。 生成(new)一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。 静态内部类的对象可以直接生成:

Outer.Inner in = new Outer.Inner();

而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。静态内部类不可用private来进行定义。 注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。 用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。 例子: 对于两个类,拥有相同的方法:

class People {
    run();
}
interface Machine {
    run();
}

此时有一个robot类:

class Robot extends People implement Machine.

此时run()不可直接实现。

interface Machine {
    void run();
}
class Person {
    void run() { System.out.println("run"); }
}
class Robot extends Person {
    private class MachineHeart implements Machine {
        public void run() { System.out.println("heart run"); }
    }
    public void run() { System.out.println("Robot run"); }
    Machine getMachine() { return new MachineHeart(); }
}
class Test {
    public static void main(String[] args) {
        Robot robot = new Robot();
        Machine m = robot.getMachine();
        m.run();
        robot.run();
    }
}

局部内部类

在方法中定义的内部类称为局部内部类。 与局部变量类似,在局部内部类前不加修饰符public和private,其范围为定义它的代码块。 注意:局部内部类不仅可以访问外部类私有实例变量,但可以访问外部类的局部常量(也就是局部变量必须为final的) 在类外不可直接访问局部内部类(保证局部内部类对外是不可见的)。 在方法中才能调用其局部内部类。 通过内部类和接口达到一个强制的弱耦合,用局部内部类来实现接口,并在方法中返回接口类型,使局部内部类不可见,屏蔽实现类的可见性。 局部内部类写法:

public class TestLocalInnerClass {
    public static void main(String[] args) {
        Outer o = new Outer();
        final int a = 9;
        o.print(a);
    }
}
class Outer {
    private int index = 100;
    public void print(final int a) {
        final int b = 10;
        System.out.println(a);
        class Inner {
            public void print() {
                System.out.println(index);
                System.out.println(a);
                System.out.println(b);
            }
        }
        Inner i = new Inner();
        i.print();
    }
}

匿名内部类

匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。 匿名内部类的特点:

  1. 一个类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的事先或是覆盖。
  2. 只是为了获得一个对象实例,不许要知道其实际类型。
  3. 类名没有意义,也就是不需要使用到。 注:一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。 因其为局部内部类,那么局部内部类的所有限制都对其生效。 匿名内部类是唯一一种无构造方法类。 大部分匿名内部类是用于接口回调用的。 匿名内部类在编译的时候由系统自动起名Out$1.class。 如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。 因匿名内部类无构造方法,所以其使用范围非常的有限。 当需要多个对象时使用局部内部类,因此局部内部类的应用相对比较多。匿名内部类中不能定义构造方法。 匿名内部类的写法:
interface A {
    void ia();
}
class B {
    public A bc() {
        return new A() {
            void ia() {
            }
        };
    }
}

使用匿名内部类:

B b = new B();
A a = b.bc();
a.ia();

javassist-3.11.0.GA.jar 起什么作用

Javassist的(JAVA编程助手)使Java字节码操纵简单。这是一个编辑Java字节码的类库。