
能力说明:
了解变量作用域、Java类的结构,能够创建带main方法可执行的java应用,从命令行运行java程序;能够使用Java基本数据类型、运算符和控制结构、数组、循环结构书写和运行简单的Java程序。
暂时未有相关云产品技术能力~
阿里云技能认证
详细说明这是一个基础类的源码文件 public class Demo { public static void main(String[] args) { System.out.println("HelloWorld!"); } } 反编译过后的 # javap -c Demo Compiled from "Demo.java" public class Demo extends java.lang.Object{ public Demo(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String helloWorld! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return } 反编译过后可以看到 自定义一个类会默认继承 java.lang.Object, 这也是 Object 利用多态能接受任意对象的原因 一个类中会默认有一个构造方法,他的默认实现是调用到父类的空构造方法 当你自定义了一个构造方法后jdk就不会默认给你加空构造了 这里要注意构造方法中super(...)一定是第一行代码,就算你不写super(...)她会默认调用父类的空构造 接下来在来看一段源码 import java.util.Arrays; import java.util.List; public class Demo { public static void main(String[] args) { List<String> list = Arrays.asList("Java", "JavaME", "JavaSE", "JavaEE"); String data = ""; for (String s : list) { data += s + ","; } System.out.println(data); } } 反编译后的结果为 # javap -c Demo Compiled from "Demo.java" public class Demo extends java.lang.Object{ public Demo(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_4 1: anewarray #2; //class java/lang/String 4: dup 5: iconst_0 6: ldc #3; //String Java 8: aastore 9: dup 10: iconst_1 11: ldc #4; //String JavaME 13: aastore 14: dup 15: iconst_2 16: ldc #5; //String JavaSE 18: aastore 19: dup 20: iconst_3 21: ldc #6; //String JavaEE 23: aastore 24: invokestatic #7; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List; 27: astore_1 28: ldc #8; //String 30: astore_2 31: aload_1 32: invokeinterface #9, 1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 37: astore_3 38: aload_3 39: invokeinterface #10, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z 44: ifeq 86 47: aload_3 48: invokeinterface #11, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 53: checkcast #2; //class java/lang/String 56: astore 4 58: new #12; //class java/lang/StringBuilder 61: dup 62: invokespecial #13; //Method java/lang/StringBuilder."<init>":()V 65: aload_2 66: invokevirtual #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 69: aload 4 71: invokevirtual #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 74: ldc #15; //String , 76: invokevirtual #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 79: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 82: astore_2 83: goto 38 86: getstatic #17; //Field java/lang/System.out:Ljava/io/PrintStream; 89: aload_2 90: invokevirtual #18; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 93: return } 从反编译中能看出 关于字符串拼接 在少量字符串拼接时jdk会默认给我们转成StringBuilder来实现,这也是为什么少量字符串拼接可以使用String对象的原因 在使用 for (String s : list) {}时 会默认调用到java.util.Iterator接口中的方法 我们自定义类如果想使用这种迭代方式就需要满足一下两种条件 是集合架构中的一员 自定义实现Iterator中的方法 自定义枚举类 public enum Demo { SUCCESS(0, "成功"), ERROR(1, "失败"); private Integer code; private String msg; Demo(Integer code, String msg) { this.code = code; this.msg = msg; } } 反编译枚举后可以看到 # javap Demo Compiled from "Demo.java" public final class Demo extends java.lang.Enum{ public static final Demo SUCCESS; public static final Demo ERROR; public static final Demo[] values(); public static Demo valueOf(java.lang.String); static {}; } 从反编译结果中可以看到 枚举类实际上是一个加了 final class 他会默认继承自java.lang.Enum 每一个枚举类型都会转成 public static final 的对象 自定义注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Demo { String value(); } 反编译的结果 # javap Demo Compiled from "Demo.java" public interface Demo extends java.lang.annotation.Annotation{ public abstract java.lang.String value(); } 从反编译结果中可以看出 可以看出注解会默认继承java.lang.annotation.Annotation 一个属性就是一个抽象方法 还有一些常识 Java 会默认导入 java.lang 包 这个自动导入使得我们在使用 Integer,Object,System 等类的时候不需要手动导包 System.out.println(); 的时候会默认调用到对象中的 toString(); 方法 HashSet在存储时会默认调用到对象的hashCode()::equals()方法 HashSet 在判断元素重复时借助了hashCode()的hash算法来筛选掉一批不重复的数据 在hash值相等的时候在借助equals()判断是否重复如果重复就不录入了 为什么要使用到两个方法来判断是否重复这里是hash算法的一个特点了, 在HashMap中如果hash值相等值不相等就会在hash表中形成一个hash链 TreeSet在存储时,要求元素实现Comparable接口 TreeSet使用了树状结构需要使用到 java.lang.Comparable#compareTo 方法的返回值 通过返回来决定元素是否重复: [0元素重复,<0左子树,>0右子树] Integer 的自动封装和catch 先来看一段源码代码 Integer val1 = 1; Integer val2 = 1; System.out.println(val1 == val2); Integer val3 = 996; Integer val4 = 996; System.out.println(val3 == val4); 反编译结果 # javap -c Demo Compiled from "Demo.java" public class Demo { public Demo(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_1 1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 4: astore_1 5: iconst_1 6: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 9: astore_2 10: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_1 14: aload_2 15: if_acmpne 22 18: iconst_1 19: goto 23 22: iconst_0 23: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V 26: sipush 996 29: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 32: astore_3 33: sipush 996 36: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 39: astore 4 41: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 44: aload_3 45: aload 4 47: if_acmpne 54 50: iconst_1 51: goto 55 54: iconst_0 55: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V 58: return } 在Integer类型自动拆装箱实际上是使用了Integer.valueOf()方法 我们可以通过了解 valueOf() 的源码来理解 Integer -128~127缓存 Integer.valueOf() 的实现 public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } Integer缓存核心实现 private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} } 内部类访问变量时的隐式操作 public static void main(String[] args) { String str = "hello"; new Transfer() { @Override void transfer() { System.out.println(str); str = "world!"; System.out.println(str); } }.transfer(); } public static abstract class Transfer { abstract void transfer(); } 在案例中 String 类型的 str 实际上会隐式的给我们加上 final 修饰
对象介绍 Object desc format LocalDateTime 日期时间 yyyy-MM-dd'T'HH:mm:ss LocalDate 日期 yyyy-MM-dd LocalTime 时间 HH:mm:ss 获取对象实例 Function Desc now 获取当前时间 of 获取指定时间 判断 Prefix is Suffix desc after 是否在传入对象, 之后 before 是否在传入对象, 之前 equal 相等 supported 检查是否受支持( 字段支持, 单位支持 ) Demo LocalDateTime maxTime = LocalDateTime.now().plusSeconds(SendMessageConfig.getDelayTime()); if (messageDTO.getSendTime().isAfter(maxTime)) { return ViewUtils.build(ViewCodeEnum.ERROR.getCode(), "验证码超时", null); } 运算 Operation Prefix plus 加 Prefix minus 减 prefix get 获取 时间单位 Suffix Desc nanos 纳 seconds 秒 hours 小时 minutes 分钟 days 天 weeks 周 months 月 years 年 获取时间戳 Instant.now().toEpochMilli() System.currentTimeMillis()
Redis 缓存 雪崩 穿透 正常情况下使用 Redis 出现雪崩与穿透的情况 雪崩: 大量 cache 在同一时间失效 穿透: 大量请求到 Redis 中, 而 Redis 中没有缓存, 最后请求落到数据库中导致崩溃 解决雪崩与穿透: 雪崩:: 可以设置 Redis cache 的过期时间,让缓存失效的时间尽量均匀 穿透:: 将一份 key 作两次缓存, 双缓存策略。
关闭两端 Linux 的防火墙 service iptables stop 关闭两端 redis.conf 的受保护机制 protected-mode no 在从 Redis 中配置 replicaof <masterip> <masterport> - <masterip> 主 Redis IP - <masterport> 端口 拷贝解压目录下的配置文件: sentinel.conf [root@admin redis-5.0.3]$ cp sentinel.conf /usr/local/redis/ 更改配置文件中的 sentinel monitor <master-name> <ip> <redis-port> <quorum> - <master-name> 可以自定义 - <ip> 哨兵检测的 IP , 但那 Redis 挂掉了给他从新选择 主 - <redis-port> 检测的端口号 - <quorum> 选择新主需要几个哨兵投票 sentinel monitor mymaster 192.168.238.130 6379 1 将主从启动起来后验证能跑 开启哨兵给主服务站岗 将主服务shutdown掉 默认等: 30 秒哨兵会投票选择新的主 Redis
在gitHub中创建项目并存放配置文件 搭建一个注册中心 搭建一个服务与git仓库进行连接 搭建一个服务通过仓库连接服务调用配置文件 架构图 在gitHub中创建项目并存放配置文件 搭建一个注册中心 :: 服务注册中的地址 eureka.client.serviceUrl.defaultZone=http://localhost:7070/eureka/ 搭建一个服务与git仓库进行连接 在 pom 文件中添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> 添加application.yaml配置文件 server: port: 8000 spring: application: name: spring-cloud-config-server cloud: config: server: git: uri: https://github.com/jiangruyi/SpringCloud.git search-paths: spring-cloud-config-core username: 2491920818@qq.com password: xxx eureka: client: service-url: defaultZone: http://localhost:7070/eureka/ 编写SpringBoot启动类 @EnableConfigServer @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } 搭建一个服务通过仓库连接服务调用配置文件 添加 pom 依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> 编写 bootstrap.yaml 配置文件 spring: cloud: config: name: application profile: dev uri: http://localhost:8000/ label: master server: port: 9000 spring.cloud.config.uri :: 与git连接的服务地址 编写基本配置文件 application.yaml spring: application: name: config-client-git server: port: 9000 eureka: client: service-url: defaultZone: http://localhost:7070/eureka/ 编写 SpringBoot 启动类读取git上的配置文件 @EnableDiscoveryClient @SpringBootApplication @RestController public class Application { @Value("${com.znsd.config}") private String gitValue; public void setGitValue(String gitValue) { this.gitValue = gitValue; } @GetMapping("hello") public String hello () { return gitValue; } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } 配置热部署 在调用配置服务端 pom 文件中添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> 在要动态热部署的配置类中添加: @RefreshScope 注解 以 POST 方式访问URL http://localhost:9000/refresh刷新配置 注意: 返回消息中包含: Full authentication is required to access this resource. 解决方案: 将安全认证关掉: management.security.enabled=false 配置一个安全认证
常见的五种单例模式实现方式– 主要: 饿汉式(线程安全,调用效率高。 但是,不能延时加载。) 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。) 其他: 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用) 静态内部类式(线程安全,调用效率高。 但是,可以延时加载) 枚举式(线程安全,调用效率高,不能延时加载。并且可以天然的防止反射和反序列化漏洞!) 如何选用? 单例对象 占用 资源 少,不需要 延时加载 枚举式 好于 饿汉式 单例对象 占用 资源 大,需要 延时加载: 静态内部类式 好于 懒 饿汉 public class OneObject { private static final OneObject OBJECT = new OneObject(); private OneObject () {} public static OneObject build () { return OBJECT; } } 懒汉 public class OneObjectLaz { private static OneObjectLaz laz; private OneObjectLaz () {} public static OneObjectLaz build () { synchronized (OneObjectLaz.class) { if (null == laz) laz = new OneObjectLaz(); } return laz; } } 多线程测试 for (int i = 0; i < 10; i++) { new Thread(() -> { OneObjectLaz build = OneObjectLaz.build(); System.out.println(build.hashCode()); }).start(); } 通过反射机制暴力范围 private 构造器, 从而达到多实例 Constructor<OneObject> constructor = OneObject.class.getDeclaredConstructor(); constructor.setAccessible(true); OneObject newInstance = constructor.newInstance(); System.out.println(newInstance); 解决方案 public class OneObjectLaz { private static OneObjectLaz laz; private OneObjectLaz () { if (laz != null) throw new RuntimeException("对象必须保证单例!"); } public static OneObjectLaz build () { synchronized (OneObjectLaz.class) { if (null == laz) laz = new OneObjectLaz(); } return laz; } } 防止序列化破解单例 public class OneObjectLaz { private static OneObjectLaz laz; private OneObjectLaz () { if (laz != null) throw new RuntimeException("对象必须保证单例!"); } public static OneObjectLaz build () { synchronized (OneObjectLaz.class) { if (null == laz) laz = new OneObjectLaz(); } return laz; } /** * 防止单例, 反序列化破解 * 如果定义了: readResolve 方法在反序列时直接返回方法指定的对象, 不需要new了 */ private Object readResolve() throws ObjectStreamException { return laz; } } 要想真正的实现单利漏洞还是很多的所以jdk提供了新特性: enum public enum One { ONE, TWO, THREE; }
责任链的应用场景 Servlet API 中的 Filter 过滤器 MVC 框架中的拦截器 . . . 简单使用责任链模式拆分 Servlet API 中的过滤器 模拟 Servlet 中的 Request 对象 /** * @desc <b>模拟 Servlet 中的 Request 对象</b> * * @author jiang ru yi */ public class HttpServletRequest { private String requestContext; private Map<String, Object> requestParam = new HashMap<>(); public String getRequestContext() { return requestContext; } public void setRequestContext(String requestContext) { this.requestContext = requestContext; } public void setRequestParam(Map<String, Object> requestParam) { this.requestParam = requestParam; } public Object setAttribute(String key, Object value) { return requestParam.put(key, value); } public Object getAttribute(String key) { return requestParam.get(key); } public Object removeAttribute(String key) { return requestParam.remove(key); } } 模拟 Servlet 中的 Response 对象 /** * @desc <b>模拟 Servlet 中的 Response 对象</b> * * @author jiang ru yi */ public class HttpServletResponse { private String responseContext; public String getResponseContext() { return responseContext; } public void setResponseContext(String responseContext) { this.responseContext = responseContext; } } 过滤器抽象层 /** * @desc <b>公用的过滤器抽象层</b> * * @author jiang ru yi */ public abstract class HttpFilter { public abstract void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain); } 过滤器调度 /** * @desc <b>过滤器的调度器</b> * * @author jiang ru yi */ public class FilterChain { private List<HttpFilter> filters = new ArrayList<>(); private int currFilter; public boolean addFilter(HttpFilter filter) { return filters.add(filter); } public boolean removeFilter(HttpFilter filter) { return filters.remove(filter); } public void doFilter(HttpServletRequest request, HttpServletResponse response) { if (currFilter++ == filters.size()) return; filters.get(currFilter - 1).doFilter(request, response, this); } } Junit 测试 public static void main(String[] args) { HttpServletRequest request = new HttpServletRequest(); request.setRequestContext("<EvE>, Y(OvO)Y"); request.setAttribute("user", "administrator"); HttpServletResponse response = new HttpServletResponse(); FilterChain chain = new FilterChain(); chain.addFilter(new CharacterSetFilter()); chain.addFilter(new PowerFilter()); chain.doFilter(request, response); System.out.println(request.getRequestContext()); } 抽象层子类 : 字符过滤器 /** * @desc <b>过滤请求中的危险符号( < > )</b> * * @author jiang ru yi */ public class CharacterSetFilter extends HttpFilter { @Override public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { String context = request.getRequestContext(); String result = context.replaceAll("<", "&le;").replaceAll(">", "&lt;"); request.setRequestContext(result); chain.doFilter(request, response); } } 抽象层子类 : 校验用户是否登录 /** * @desc <b>过滤用户是否登录</b> * * @author jiang ru yi */ public class PowerFilter extends HttpFilter { @Override public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { Object attribute = request.getAttribute("user"); if (null != attribute) { chain.doFilter(request, response); } else { throw new RuntimeException("user not login"); } } }
在项目src/main/resources目录下建立banner.txt文件, SpringBoot 的启动输出就被更改了 佛祖 log ${AnsiColor.BRIGHT_YELLOW} ${AnsiColor.BRIGHT_RED}_ooOoo_${AnsiColor.BRIGHT_YELLOW} ${AnsiColor.BRIGHT_RED}o8888888o${AnsiColor.BRIGHT_YELLOW} ${AnsiColor.BRIGHT_RED}88${AnsiColor.BRIGHT_YELLOW}" . "${AnsiColor.BRIGHT_RED}88${AnsiColor.BRIGHT_YELLOW} (| -_- |) O\ = /O ____/`---'\____ .' \\| |// `. / \\||| : |||// \ / _||||| -:- |||||- \ | | \\\ - /// | | | \_| ''\---/'' | | \ .-\__ `-` ___/-. / ___`. .' /--.--\ `. . __ ."" '< `.___\_<|>_/___.' >'"". | | : `- \`.;`\ _ /`;.`/ - ` : | | \ \ `-. \_ __\ /__ _/ .-` / / ======`-.____`-.___\_____/___.-`____.-'====== `=---=' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 佛祖保佑 永无BUG 类似 ${AnsiColor.BRIGHT_YELLOW} 这种表达式,可修改启动界面上的颜色,会根据AnsiColor的设置改变界面的颜色,这样启动的界面就会显示多种不同的颜色了。 可以进入该网站生成一些简单的字符: http://www.network-science.de/ascii/ http://patorjk.com/software/taag/ http://www.makepic.net/tool/image2ascii.html : 该网站可以将图片转成字符 例: s u c c e s s ______ __ __ ____ ____ ____ ______ ______ / ___/ | | \ _/ ___\ _/ ___\ _/ __ \ / ___/ / ___/ \___ \ | | / \ \___ \ \___ \ ___/ \___ \ \___ \ /____ > |____/ \___ > \___ > \___ > /____ > /____ > \/ \/ \/ \/ \/ \/ 手指 log d*##$. zP"""""$e. $" $o 4$ '$ $" $ '$ '$ J$ $F 'b $k $> $ $K $r J$ d$ '$ $ $" $~ '$ "$ '$E $ $ $L $" $F ... $. 4B $ $$$*"""*b '$ $. $$ $$ $F "$ RS $F $" $ $k ?$ u* dF .$ ^$. $$" z$ u$$$$e #Sb $E.dW@e$" ?$ #$ .o$$# d$$$$c ?F $ .d$$#" . zo$> #$r .uF $L .u$*" $&$$$k .$$d$$F $$" ""^"$$$P"$P9$ JP .o$$$$u:$P $$ $ ..ue$" "" $" d$ $F $ $$ ...udE 4B #$ """` $r @$ ^$L '$ $F RN 4N $ *Sb d$ $$k $F $$b $F $"" $F '$ $ $L $ '$ $ $ $
编写 .properties 文件 文件编写以 [ key=value ] 格式 = 符号是允许有空格的, 程序读取时会去除 = 左右空格 数据以换行来标识结束 定义类获取.peroperties文件中的值 获取本类 Class 对象 通过 Class 对象获取 ClassLoader 类加载器 通过类加载器的 getResourceAsStream() 方法获取字节流 定义 Properties 集合, 调用集合中的 load(inputStream) 来读取文件 通过 Properties 集合中的 getProperty("key") 来获取对于的值 编写 .properties 文件 key=value mark=markValue info=infoVlaue 定义类获取.peroperties文件中的值 public static void main(String[] args) throws IOException { InputStream inputStream = PropertiesDemo.class.getClassLoader().getResourceAsStream("info.properties"); Properties properties = new Properties(); properties.load(inputStream); System.out.println("key:[" + properties.getProperty("key") + "]"); System.out.println("mark:[" + properties.getProperty("mark") + "]"); System.out.println("info:[" + properties.getProperty("info") + "]"); } 那么执行程序将输出结果为: key:[value] mark:[markValue] info:[infoVlaue]
导入 SpringBoot 的 snakeyaml 解析包 编写 yaml 文件 编写 yaml 文件对应的 class 编写 main 进行获取 导入 SpringBoot 的 snakeyaml 解析包 <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.23</version> </dependency> 编写 yaml 文件 id: 110 name: 'stuName' gender: '男' clas: id: 110 name: 'classesName' describe: 'describe' 编写 yaml 文件对应的 class public class Student { private Integer id; private String name; private String gender; private Classes clas; // getter(), setter(), toString(); } class Classes { private Integer id; private String name; private String describe; // getter(), setter(), toString(); } 编写 main 进行获取 public static void main(String[] args) { Yaml yaml = new Yaml(); Student result = yaml.loadAs(thisClass.class.getClassLoader() .getResourceAsStream("springboot.yaml"), Student.class); System.out.println(result); }
在项目添加 maven 配置 <!-- 解析数据 --> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.8.3</version> </dependency> <!-- 爬网页 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version> </dependency> 使用 HttpClient 发起请求获取页面数据 HttpGet httpGet = new HttpGet("https://www.baidu.com/"); HttpHost proxy = new HttpHost("125.70.13.77", 8080);// 使用代理服务器发送请求 httpGet.setConfig(RequestConfig.custom() .setSocketTimeout(30000)// 发送时间 .setConnectTimeout(30000)// 连接时间 .setProxy(proxy)// 设置代理服务器 .build()); CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpClientContext context = HttpClientContext.create(); CloseableHttpResponse response = httpClient.execute(httpGet, context); HttpEntity entity = response.getEntity(); String html = EntityUtils.toString(entity, "utf-8"); System.out.println(html); 不使用本机发起请求, 而是使用代理服务器发起请求是应为: 防止被一些反扒网站拉黑: 代理服务器 使用Jsoup发起请求并且解析页面数据 // 获取 Document 对象 Document document = Jsoup.connect("https://www.baidu.com/").get(); // 通过 CSS 选择器, 匹配标签数据 Elements select = document.select("a"); for (Element element : select) { System.out.println(element); } Document对象中有各种JavaScript 和 CSS解析方法 一般发送请求使用httpclient解析网页数据使用jsoup, 两者组合使用 HttpGet httpGet = new HttpGet("https://www.baidu.com/"); HttpHost proxy = new HttpHost("125.70.13.77", 8080); httpGet.setConfig(RequestConfig.custom() .setSocketTimeout(30000) .setConnectTimeout(30000) .setProxy(proxy) .build()); CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpClientContext context = HttpClientContext.create(); CloseableHttpResponse response = httpClient.execute(httpGet, context); HttpEntity entity = response.getEntity(); String htmlData = EntityUtils.toString(entity, "utf-8"); Document document = Jsoup.parse(htmlData); Elements select = document.select("a"); for (Element element : select) { System.out.println(element); }
题: 定义两个 Integer 对象传递给一个方法, 方法执行后两个 Integer 的值需要互相转换 public static void practise(MyConsumer<Integer> consumer) { Integer value1 = 1; Integer value2 = 2; System.out.println("start: value1=" + value1 + ", value2=" + value2); consumer.accept(value1, value2); System.out.println("end: value1=" + value1 + ", value2=" + value2); } @FunctionalInterface public interface MyConsumer<T> { void accept (T value1, T value2); } 编程方式一: 传值与传引用的区别 practise((value1, value2) -> { Integer temp = value1; value1 = value2; value2 = temp; return; }); 输入结果: start: value1=1, value2=2 end: value1=1, value2=2 编程方式二: Integer 在 -127 ~ 128 之间的缓存 practise((value1, value2) -> { try { Field declaredField = Integer.class.getDeclaredField("value"); declaredField.setAccessible(true); Integer intValue = value1.intValue(); declaredField.set(value1, value2); declaredField.set(value2, intValue); } catch (Exception e) {} return; }); 输出结果: start: value1=1, value2=2 end: value1=2, value2=2 编程方式三: 清空缓存 practise((value1, value2) -> { try { Field declaredField = Integer.class.getDeclaredField("value"); declaredField.setAccessible(true); Integer intValue = new Integer(value1.intValue()); declaredField.set(value1, value2); declaredField.set(value2, intValue); } catch (Exception e) {} return; }); 输出结果: start: value1=1, value2=2 end: value1=2, value2=1
七牛云官网 下载官方 SDK | java.zip 在官网 控制台创建 CDN 存储空间 将 SDK jar 包导入完成后找到: java-sdk-X.x.xx\examples\upload.java 在官网 控制台创建 CDN 存储空间 将 SDK jar 包导入完成后找到 : java-sdk-X.x.xx\examples\upload.java examples 下定义了很多操作的 案例这里我只试一个上传 : 需要更改的地方有 String ACCESS_KEY = "Access_Key"; String SECRET_KEY = "Secret_Key"; String bucketname = "Bucket_Name"; 之前创建的存储空间 String key = "my-java.png"; 上传到七牛后保存的文件名 String FilePath = "/.../..."; 上传文件的路径 执行 main 方法 OK 注意: 执行 main 方法的时候抛出了异常 检查你的电脑网络是否畅通 检查jar包是否版本过低: gson-2.8.2.jar happy-dns-java-0.1.4.jar okhttp-2.3.0-SNAPSHOT.jar okhttp-3.4.1.jar okio-1.6.0.jar
创建页面导入 jquery 定义 input type=file 编写 ajax 请求 定义后台 servlet 接收文件 创建页面导入 jquery , 定义 input type=file , 编写 ajax 请求 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>this is ajax upload</title> </head> <body> <input type="file" id="file" name="file" /> <br /> <button id="submitFile">ajax upload</button> <script src="js/jquery-3.3.1.min.js"></script> <script type="text/javascript"> $("#submitFile").click(function() { var formData = new FormData(); formData.append("file", document.getElementById("file").files[0]); formData.append("param", "paramValue"); $.ajax({ url: "fileUpload", type: "POST", data: formData, /*contentType 类型:String 默认值: "application/x-www-form-urlencoded"。发送信息至服务器时内容编码类型。 */ contentType: false, /*processData 类型:Boolean 默认值: true。默认情况下,通过data选项传递进来的数据, 如果是一个对象(技术上讲只要不是字符串), 都会处理转化成一个查询字符串, 以配合默认内容类型 "application/x-www-form-urlencoded"。 如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。 */ processData: false, success: function (data) { if (data == "success") { alert("上传成功"); } }, error: function () { alert("上传失败"); } }); }); </script> </body> </html> 与平常编写的 ajax 的区别在于 使用 new FormData(); 来进行传值 多了一些属性 contentType: false processData: false document.getElementById("file").files[0] document.getElementById("file") : 是一个 JavaScript 对象 在传递值是需要拿到上传文件的 字节数据 .files[0] document.getElementById("file").files[0] 执行完会是一个 file 对象可以 .size可获得文件的大小 定义后台 servlet 接收文件 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Part part = request.getPart("file"); InputStream inputStream = part.getInputStream(); byte[] bytes = new byte[1024]; inputStream.read(bytes); System.out.println(new String(bytes)); System.out.println("param:" + request.getParameter("param")); response.getWriter().write("success"); }
需要依赖jar: commons-codec.jarbeans64 工具类 package com.znsd.beans64; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.codec.binary.Base64; /** * beans64 工具类 */ public class Base64Util { /** * 将文件转换 beans64 * @param imgFile 待处理的数据 * @return beans64数据 */ public static String filetoBese64(String dir) { InputStream in = null; byte[] bytes = null; try { in = new FileInputStream(dir); bytes = new byte[in.available()]; in.read(bytes); in.close(); } catch (IOException e) { e.printStackTrace(); } return new String(Base64.encodeBase64(bytes)); } /** * 将 beans64 数据转为 磁盘数据 * @param beans64String * @param dir * @throws IOException */ public static void bese64ToFile(String beans64String, String dir) throws IOException { if (beans64String == null) return ; byte[] bytes = Base64.decodeBase64(beans64String); for (int i = 0; i < bytes.length; ++i) { if (bytes[i] < 0) bytes[i] += 256; } OutputStream out = new FileOutputStream(dir); out.write(bytes); out.flush(); out.close(); } /** * 将String 数据转换为 beans64 * @param beans64String 需要转换的 beans64 * @return 处理好的 String */ public static String stringTobeans64(String beans64String) { return new String(Base64.encodeBase64(beans64String.getBytes())); } /** * 将beans64 数据转换为 String * @param beans64String 需要转换的 beans64 * @return 处理好的 String */ public static String beans64ToString(String beans64String) { String temp = ""; if (beans64String == null) return temp; byte[] bytes = Base64.decodeBase64(beans64String); for (int i = 0; i < bytes.length; ++i) { if (bytes[i] < 0) bytes[i] += 256; } return new String(bytes); } } /** * demo 案例 * @param args */ public static void main(String[] args) { System.out.println(beans64ToString(stringTobeans64("StringData"))); String fileBese64 = filetoBese64("c:/filePath"); bese64ToFile(fileBese64, "toDir"); } beans64 与 md5 整合: 个人觉得没必要了做加密 DM5 就够了 package com.znsd.beans64; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.codec.binary.Base64; /** * 消息摘要工具 */ public class MessageDigestUtil { /** * 先进行MD5摘要再进行Base64编码获取摘要字符串 * * @param str * @return */ public static String base64AndMD5(String str) { if (str == null) { throw new IllegalArgumentException("inStr can not be null"); } return base64AndMD5(toBytes(str)); } /** * 先进行MD5摘要再进行Base64编码获取摘要字符串 * * @return */ public static String base64AndMD5(byte[] bytes) { if (bytes == null) { throw new IllegalArgumentException("bytes can not be null"); } try { final MessageDigest md = MessageDigest.getInstance("MD5"); md.reset(); md.update(bytes); final Base64 base64 = new Base64(); final byte[] enbytes = base64.encode(md.digest()); return new String(enbytes); } catch (final NoSuchAlgorithmException e) { throw new IllegalArgumentException("unknown algorithm MD5"); } } /** * UTF-8编码转换为ISO-9959-1 * * @param str * @return */ public static String utf8ToIso88591(String str) { if (str == null) { return str; } try { return new String(str.getBytes("UTF-8"), "ISO-8859-1"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage(), e); } } /** * ISO-9959-1编码转换为UTF-8 * * @param str * @return */ public static String iso88591ToUtf8(String str) { if (str == null) { return str; } try { return new String(str.getBytes("ISO-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.getMessage(), e); } } /** * String转换为字节数组 * * @param str * @return */ private static byte[] toBytes(final String str) { if (str == null) { return null; } return str.getBytes(); } } MD5加密 与 SHA1加密 import org.apache.commons.codec.digest.DigestUtils; /** * MD5 案例 * @param args */ public static void main(String[] args) { // 1. MD5加密 DigestUtils.md5Hex(data) System.out.println(DigestUtils.md5Hex("StringData")); // 2. SHA1加密 DigestUtils.sha1Hex(data) System.out.println(DigestUtils.sha1Hex("StringData")); }
要了解 Java中的 volatile 首席要从线程间通讯开始 概念: 对于声明了 volatile 的变量进行写的操作时, JVM 会向处理器(CPU) 发送一条 Lock 前缀的指令, 会将这个变量所在缓存行的数据写回到系统内存(主内存) 在多处理器的情况下, 保持各个处理器缓存一致性的特点, 就会实现缓存一致性协议 但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。 每个处理器通过嗅探在主内存上传播的数据来检查自己缓存的值是不是过期了 当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,就会重新从系统内存中把数据读到处理器缓存里。 总之 volatile 做的事情是: 在多线程执行中保证共享对象的一致性 synchronized public class Demo { private static int i = 0; public static void main(String[] args) { synchronized(Demo.class) { i++; } } } javap -c -v Demo.class : 结果 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: ldc #2 // class Demo 2: dup 3: astore_1 4: monitorenter 5: getstatic #3 // Field i:I 8: iconst_1 9: iadd 10: putstatic #3 // Field i:I 13: aload_1 14: monitorexit 15: goto 23 18: astore_2 19: aload_1 20: monitorexit 21: aload_2 22: athrow 23: return Exception table: from to target type 5 15 18 any 18 21 18 any LineNumberTable: line 6: 0 line 7: 5 line 8: 13 line 9: 23 StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 18 locals = [ class "[Ljava/lang/String;", class java/lang/Object ] stack = [ class java/lang/Throwable ] frame_type = 250 /* chop */ offset_delta = 4 monitorenter : 进入并获取对象监视器 monitorexit : 释放并退出对象监视器 synchronized获得并释放监视器, 如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行。 volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。 显然synchronized要比volatile消耗更多资源。
javassist 可以实现 java 的动态性 比如在 java 程序运行时, 动态的添加新方法修改类结构 该类 API 与 java.lang.Class API 相似 动态的创建一个 class 对象 ClassPool pool = ClassPool.getDefault(); /** 声明类名及包名 */ CtClass ctClass = pool.makeClass("com.znsd.javassist.Emp"); /** 创建属性 */ CtField ctField = CtField.make("private Integer id;", ctClass); ctClass.addField(ctField); /** 创建方法 */ CtMethod ctMethod = CtMethod.make("public Integer getId(){return id;}", ctClass); ctClass.addMethod(ctMethod); // 将生成好的类输出在本地磁盘上 ctClass.writeFile("F:/"); System.out.println("执行成功!"); 获取类的基本信息 ClassPool classPool = ClassPool.getDefault(); CtClass ctClass = classPool.get("com.znsd.javassist.Student"); System.out.println("获取类路径: " + ctClass.getName()); System.out.println("获取类名: " + ctClass.getSimpleName()); System.out.println("获取父类: " + ctClass.getSuperclass()); System.out.println("获取接口: " + ctClass.getInterfaces()); 动态创建方法 ClassPool classPool = ClassPool.getDefault(); CtClass ctClass = classPool.get("com.znsd.javassist.Student"); /** 声明好一个方法 */ CtMethod ctMethod = CtNewMethod.make("public int add(int i, int j){return i + j;}", ctClass); ctClass.addMethod(ctMethod); /** 获取 Class 对象用于调用创建好的方法 */ Class classes = ctClass.toClass(); Object newInstance = classes.newInstance(); Method declaredMethod = classes.getDeclaredMethod("add", int.class, int.class); Object result = declaredMethod.invoke(newInstance, 110, 120); System.out.println(result); 实现 aop 编程 ClassPool classPool = ClassPool.getDefault(); CtClass ctClass = classPool.get("com.znsd.javassist.Student"); CtMethod ctMethod = ctClass.getDeclaredMethod("show"); // 在方法前插入一些代码 ctMethod.insertBefore("System.out.println(\"aop:before\");"); // 在方法后插入一些代码 ctMethod.insertAfter("System.out.println(\"aop:after\");"); // 指定在哪一行插入一些代码 ctMethod.insertAt(29, "System.out.println(\"insertAt\");"); Class classes = ctClass.toClass(); Object newInstance = classes.newInstance(); Method method = classes.getDeclaredMethod("show"); method.invoke(newInstance);
markdown-开发 解压 editor.md-master.zip 导入 web 项目 编辑 html 页面 编写 servlet 获取值 解压 editor.md-master.zip AND 导入 web 项目 editor.md-master.zip: 下载链接 编写 html 页面使用 <!DOCTYPE html> <html lang="zh"> <head> <meta charset="utf-8" /> <title>markdown</title> <link rel="stylesheet" href="editor/examples/css/style.css" /> <link rel="stylesheet" href="editor/css/editormd.css" /> <%-- 需要导入两个 CSS --%> </head> <body> <div id="test-editormd"> <textarea style="display:none;"> <%-- 页面一开始显示的内容 --%> </textarea> </div> <button onclick="save()" class="btn">save</button> <%-- 导入jquery --%> <script src="editor/examples/js/jquery.min.js"></script> <%-- 导入 editormd 语法 --%> <script src="editor/editormd.min.js"></script> <script type="text/javascript"> var editor; /** 初始化编辑器: 1. 需要注意的 path 目录指定的是 editor 中的 lib/ */ $(function() { editor = editormd("test-editormd", { width : "90%", height : 640, syncScrolling : "single", path : "editor/lib/", saveHTMLToTextarea : true }); }); /** 获取编辑器的内容 */ function save() { var markdown = editor.getMarkdown(); var HTML = editor.getHTML(); console.log(markdown); console.log(HTML); $.ajax({ url:"markdownDemo", type:"post", data:{ "markdown":markdown, "HTML":HTML }, success:function(data) { alert(data); } }); } </script> </body> </html> 编写 servlet 获取值 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String markdown = request.getParameter("markdown"); String HTML = request.getParameter("HTML"); System.out.println(markdown); System.out.println(HTML); response.setCharacterEncoding("UTF-8"); response.getWriter().write("操作成功!"); } 初始化编辑器的值介绍 值 描述 width 编辑器的宽 height 编辑器的高 syncScrolling 同步滚动 path editor 中的 lib/ 目录 saveHTMLToTextarea 是否保存 HTML 数据 页面渲染 markdown 笔记 <!DOCTYPE html> <html lang="zh"> <head> <meta charset="utf-8" /> <title>HTML Preview(markdown to html) - Editor.md examples</title> <link rel="stylesheet" href="css/style.css" /> <link rel="stylesheet" href="editor-md/css/editormd.preview.css" /> <link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" /> <style> .editormd-html-preview { width: 90%; margin: 0 auto; } </style> </head> <body> <span id="id" class="id" ></span> <div id="layout"> <div id="test-editormd-view2"> <textarea id="append-test" style="display:none;"></textarea> </div> </div> <script src="js/jquery-2.0.3.min.js"></script> <script src="editor-md/lib/marked.min.js"></script> <script src="editor-md/lib/prettify.min.js"></script> <script src="editor-md/lib/raphael.min.js"></script> <script src="editor-md/lib/underscore.min.js"></script> <script src="editor-md/lib/sequence-diagram.min.js"></script> <script src="editor-md/lib/flowchart.min.js"></script> <script src="editor-md/lib/jquery.flowchart.min.js"></script> <script src="editor-md/editormd.js"></script> <script type="text/javascript"> $(function() { /** 1. 获取到数据 editor-md 源数据 2. 将返回的源数据添加到容器中[id:append-test] 3. 接着下面渲染视图 注意: ajax 可能需要同步请求 */ $.ajax({ type: "get", url: "markdown", data: { "id": ${动态获取的数据库ID} }, cache:false, async:false, success: function(result){ console.log(result); $("#append-test").empty().append(result.putContext); } }); var testEditormdView, testEditormdView2; $.get("editor-md/examples/test.md", function(markdown) { testEditormdView = editormd.markdownToHTML("test-editormd-view", { markdown : markdown ,//+ "\r\n" + $("#append-test").text(), //htmlDecode : true, // 开启 HTML 标签解析,为了安全性,默认不开启 htmlDecode : "style,script,iframe", // you can filter tags decode //toc : false, tocm : true, // Using [TOCM] //tocContainer : "#custom-toc-container", // 自定义 ToC 容器层 //gfm : false, //tocDropdown : true, // markdownSourceCode : true, // 是否保留 Markdown 源码,即是否删除保存源码的 Textarea 标签 emoji : true, taskList : true, tex : true, // 默认不解析 flowChart : true, // 默认不解析 sequenceDiagram : true, // 默认不解析 }); //console.log("返回一个 jQuery 实例 =>", testEditormdView); // 获取Markdown源码 //console.log(testEditormdView.getMarkdown()); //alert(testEditormdView.getMarkdown()); }); testEditormdView2 = editormd.markdownToHTML("test-editormd-view2", { htmlDecode : "style,script,iframe", // you can filter tags decode emoji : true, taskList : true, tex : true, // 默认不解析 flowChart : true, // 默认不解析 sequenceDiagram : true, // 默认不解析 }); }); </script> </body> </html>
Maven项目整合SSH 编写数据库基本连接信息 配置 Spring 核心文件 配置 web.xml 文件 添加 struts.xml or 使用注解 编写数据库基本连接信息: database.properties jdbc.username=root jdbc.password=root jdbc.url=jdbc:mysql://localhost:3306/database_db jdbc.driver=com.mysql.jdbc.Driver 配置 Spring 核心文件 配置数据库连接池 配置 Hibernate 基本信息 配置 Hibernate 事务管理 配置数据库连接池 <?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" 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 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"> <!-- 数据库配置文件位置 --> <context:property-placeholder location="classpath:database.properties" /> <!-- 数据库连接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="url" value="${jdbc.url}"></property> </bean> <!-- maven:阿里巴巴数据库 - 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!-- 数据库连接 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> 配置 Hibernate 基本信息 <!-- 配置SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <!-- 将数据源引入: sessionFactory --> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="hibernateProperties"> <props> <!-- 配置Hibernate : 数据库方言 --> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <!-- 配置Hibernate : 是否显示SQL --> <prop key="hibernate.show_sql">true</prop> <!-- 配置Hibernate : 格式化SQL --> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> <property name="mappingLocations"> <list> <!-- 指定Hibernate : 对象映射文件路径 --> <value>classpath:com/znsd/ssh/bean/*.hbm.xml</value> </list> </property> </bean> <!-- hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.3.11.Final</version> </dependency> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.18.RELEASE</version> </dependency> <!-- spring-orm --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>4.3.18.RELEASE</version> </dependency> 配置 Hibernate 事务管理 <!-- 配置事务 - 管理器 --> <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置需要开启事务的方法 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <!-- 设置匹配方法 --> <tx:method name="find*" propagation="REQUIRED" read-only="true" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="del*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="*" propagation="REQUIRED" read-only="true" /> </tx:attributes> </tx:advice> <!-- AOP切面拦截事务 --> <aop:config> <aop:pointcut id="serviceMethod" expression="execution(* com.znsd.ssh.service.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" /> </aop:config> <!-- AspectJ : 配置事务管理 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.1</version> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <!-- spring-事务 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.3.18.RELEASE</version> </dependency> 配置 web.xml 文件 添加 OpenSessionInView 过滤器 添加编码格式过滤器: 可选 配置Spring核心文件路径 Struts2 核心 过滤器 防止spring内存溢出监听器 <!-- 添加 OpenSessionInView 过滤器, 将 Session 生命周期搬到视图层 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 添加编码格式过滤器 --> <filter> <description>字符集过滤器</description> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <description>字符集编码</description> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置Spring核心文件路径 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-*.xml</param-value> </context-param> <!-- 用于初始 Spring 配置信息 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Struts2 核心 过滤器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 防止spring内存溢出监听器 --> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> 添加 struts.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/" extends="struts-default"></package> </struts> 配置 maven 项目的jdk版本 <!-- 配置 maven 项目的jdk版本 --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> maven:struts <!-- struts --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.5.16</version> </dependency> <!-- struts2 集成 spring --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> <version>2.5.16</version> </dependency> 整合 Struts2 非注解方式使用时应该注意 Spring 配置文件中配置[ action, service, dao ] 在配置 Action 的时候需要注意我们一般需要 scope="prototype" Struts2 配置文件下的 action > class 写 Spring 下的 bean > id Spring 配置文件中配置[ action, service, dao ] <bean id="springAction" class="package.className" scope="prototype"> <property name="springService" ref="springService"></property> </bean> . . . <bean id="SpringDao" class="com.znsd.ssh.dao.impl.BookDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> Struts2 配置文件下的 action > class 写 Spring 下的 bean > id <action name="updateBook" class="springAction" method="method"> <result>/index.jsp</result> </action> 整合使用 Struts2 注解方式 struts2 配置文件可以放弃 导入 struts2-convention-plugin.jar 使用时常用注解介绍 导入 struts2-convention-plugin.jar <!-- struts2 注解 --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-convention-plugin</artifactId> <version>2.5.16</version> </dependency> 使用时常用注解介绍 注解 介绍 @ParentPackage("json-default") 继承父包[struts-default:json-default] @Namespace("/") 定义命名空间 @Action 定义一个请求 @Result 定义一个返回结果 @Scope("prototype") 设置 Action 为多利模式 @ParentPackage("json-default") @Namespace("/") public class ProductAction extends ActionSupport { @Action(value = "url", results = { @Result(name = SUCCESS, location = "/index.jsp") }) @Action(value = "url", results = { @Result(name = SUCCESS, type= "json" , params = {"root", "result"}) })
获得 JavaScript 脚本引擎 ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine engine = scriptEngineManager.getEngineByName("javascript"); 定义获取变量 // 定义变量 engine.put("msg", "this is msg"); // 获取变量 System.out.println(engine.get("msg")); 定义并执行方法 // 定义函数 engine.eval("function add (num1, num2) {return num1 + num2;}"); // engine 该类同样实现了 Invocable 接口 : 转型过去拿到 API Invocable invocable = (Invocable) engine; // 调用函数 Object invokeFunction = invocable.invokeFunction("add", new Object[] {10, 10}); System.out.println(invokeFunction); 执行本地 JavaScript 代码 URL resource = JavaScriptDemo.class.getClassLoader().getResource("JavaScriptDemo.js"); FileReader fileReader = new FileReader(resource.getPath()); engine.eval(fileReader); fileReader.close(); 外部 JavaScript 代码 /** * 被java调用的外部 JavaScript */ function demo() { var i = 110; var j = 110; console.log("this is a i + j : " + (i + j)); } demo(); 执行复杂的算数表达式 Object eval = engine.eval("10 * 10 + 10"); System.out.println(eval);