首页
统计
留言板
友接
推荐
免费图床
服务监控
Search
1
immich开源相册部署教程
817 阅读
2
GraalVM将Java打包原生Native应用
327 阅读
3
将旧手机改造成Linux服务器
278 阅读
4
Java函数式编程
185 阅读
5
FRP内网穿透教程
168 阅读
编程语言
Java
Python
Go
单片机
Arduino
ESP8266
ESP32
STM32
51单片机
树莓派
运维
Docker容器
随身小记
登录
Search
标签搜索
Spring
SpringMVC
Java
docker
DSM
群晖
iptables
ssh
spring
mybaits
redis
SpringBoot
消息队列
科长
累计撰写
36
篇文章
累计收到
5
条评论
首页
栏目
编程语言
Java
Python
Go
单片机
Arduino
ESP8266
ESP32
STM32
51单片机
树莓派
运维
Docker容器
随身小记
页面
统计
留言板
友接
推荐
免费图床
服务监控
搜索到
3
篇与
Java
的结果
2023-07-10
Java函数式编程
在程序开发中使用函数式编程,可以减少冗杂的代码,加快开发速度等。接下来,我将给大家讲讲在 Java 8 中的三种函数式编程的方法,lambda 表达式、:: 符号和 Optional 类,下面是详细内容。1、函数式编程函数式编程(Functional Programming)属于编程范式(Programming Paradigm)中的用语,此外还有命令式编程(Imperative Programing)等,有兴趣的同学可以自行了解,我们这里大概解释一下函数式编程,在函数式编程中,输入一旦确定了,输出都确定了,函数调用的结果只依赖于传入的输入变量和内部逻辑,不依赖于外部,这样的写出的函数没有副作用。举个例子:public class Test { private int a = 1; public int add(int b) { return a + b; } public int pow(int c) { return c * c; } public static void main(String[] args) { Test t = new Test(); t.add(1); t.pow(2); } }上面代码中add(int b)这个方法就不符合函数式编程,这个函数调用后的结果不确定,它的结果不仅取决于b还取决于字段a。而pow(int c)这函数就是符合函数式编程的典范,只要调用它,输入的值c确定了返回值就肯定确定了。 在函数式编程中,函数也是一等公民,可以被当做参数传递,可以被赋值,被引用,可以把它当做一种数据类型来对待,如果你会使用javascript之类的语言就可以有更深的体会。如果要深入了解这种编程范式可以自行网上搜素资料了解。2、lambda表达式lambda表达式是jdk8中的新特性,上面讲函数式编程就是引入这个,oracle在jdk8中引入了lambda,从此Java中开始对函数式编程的部分支持。 Java中lambda表达式的语法结构:(params) -> expression由三部分构成,第一部分是括号以及括号内部的形式参数,第二部分是->箭头符号,第三部分是expression方法体,方法体可以是代码块也可以是执行表达式。// lambda 表达式 完整状态 有入参 有返回 有代码块 (int a, int b) -> { int c = a + b; return c; } // 当代码不需要返回值 则可省去 return (int a, int b) -> { int c = a + b; } // 当代码只有一句话的时候可以省去 大括号 (int a, int b) -> a + b; // 同样可以省略形参类型 (a, b) -> a + b; // 当只有一个参数时, 可以省去形参的小括号 a -> a * a;接下来使用lambda表达式来定义和实现一个函数式接口:@FunctionalInterface public interface ITest { int add(int a, int b); }内部匿名类实现方式:public static void main(String[] args) { ITest iTest = new ITest() { @Override public int add(int a, int b) { return a + b; } }; iTest.add(1, 1); }Lambda实现方式: public static void main(String[] args) { ITest t = (a, b) -> a + b; t.add(1, 1); }使用lambda表达式会使代码更加的优雅3、使用双冒号::符号对上面的Lambda实现方式进行进步一步的优化:public static void main(String[] args) { ITest t = Integer::sum; t.add(1, 1); }双冒号的使用方式:类名::静态方法名或者 类的实例::实例方法4、Optional类Optional也是jdk8中的一个新的类的,它给予我们更加优雅的方式来处理Java语言中的NPE异常。可以从一定程度上代替if判断, 介绍相关接口:empty 创建一个空的Optional对象of 和ofNullableof创建一个Optional对象, 如果传入的参数为空则跑出NPE异常.ofNullable和上面一样, 但是当传入参数为空的时候会调用empty方法创建一个空Optional对象.Optional<String> of = Optional.of("waxxd"); // 传入空参数会抛出NullPointerException异常 Optional<String> ofNull = Optional.of(null); // 以下两句都正常执行 Optional<String> ofNullable = Optional.ofNullable("waxxd"); // 参数为空的时候相当调用Optional.empty() Optional<String> ofNullableNull = Optional.ofNullable(null); get/orElse/orElseGet/orElseThrow// get 获取Option包裹的值如果值为null则抛出NoSuchElementException异常 String aa = Optional.of("aa").get(); // orElse 获取值如果值为空则返回orElse设置的默认值 String aa1 = Optional.of("aa").orElse("bb"); // orElseGet 获取值如果值为空则内部可以是一个实现Supplier接口的匿名内部类调用提供返回结果 String aa2 = Optional.of("aa").orElseGet( () -> "aaa".toUpperCase()); // orElseThrow获取值如果不存在则抛出后面的异常 Optional.empty().orElseThrow(IllegalArgumentException::new); // 实际的应用, 也就是上文所说的如何优化if // 比如你有个接口, 用户传入参数Integer type, 用户也可以选择不传, 不传我们为它设置默认值1 public void f(Integer type){ if(type = null) { type = 1; } Optional.ofNullable(type).orElse(1); }
2023年07月10日
185 阅读
0 评论
0 点赞
GraalVM将Java打包原生Native应用
2023年07月10日
327 阅读
0 评论
0 点赞
2023-07-10
GraalVM 是一种高性能 JDK,旨在加速 Java 应用程序性能,同时消耗更少的资源。 它提供了 Graal 编译器,它可以用作即时编译器,在 HotSpot JVM 上运行 Java 应用程序或提前将它们编译为本机可执行文件。除了Java,它还为JavaScript,Ruby,Python和其他几种具有多语言功能的流行语言提供了运行时。Graalvm下载地址 Releases · graalvm/graalvm-ce-builds (github.com))安装Graalvm环境由于本人电脑是macos则此处演示macos的安装和使用方法。在其他系统中安装方式大同小异,都是相当于添加一个新的jdk环境首先去官网下载对应自己平台和版本的包。我是MacOS M1,使用Java11环境,所所以此处我选择的是 macOS(aarch64)的包。下载完成后进行解压,移动到指定位置sudo mv graalvm-ce-java11-22.3.1 /Library/Java/JavaVirtualMachines/然后删除隔离属性 sudo xattr -r -d com.apple.quarantine /Library/Java/JavaVirtualMachines/graalvm-ce-java11-22.3.1添加环境变量添加完环境变量后使其生效如果输入java -version 输出 OpenJDK Runtime Environment GraalVM CE 22.3.1 (build 11.0.18+10-jvmci-22.3-b13)则表示环境已经添加成功了,整个步骤就是在添加一个新的Java环境IDEA集成Graalvm在IDEA新增项目,并在新增界面添加新的JDKAdd JDK,新建项目并编写一个main方法在mavenpom.xml中增加graalvm的打包插件<build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <configuration> <!-- imageName用于设置生成的二进制文件名称 --> <imageName>${project.artifactId}</imageName> <!-- mainClass用于指定main方法类路径 --> <mainClass>com.bystart.Main</mainClass> <buildArgs> --no-fallback </buildArgs> </configuration> </plugin> </plugins> </build>打包测试输入打包命令进行打包mvn -Pnative native:compile或者使用IDEA的maven工具打包运行测试
2023-07-03
Java Spring框架
1、什么是Spring?Spring 是一个支持快速开发的Java EE应用程序框架。他提供了一系列底层容器和基础设施,并可以快速集成大量常用的开源框架。是开发Java EE项目的必备。2、Spring FrameworkSpring Framework 主要包括几个模块:支持 IoC 和 AOP 的容器支持 JDBC 和 ORM 的数据访问模块支持申明式事物模块支持基于 Servlet 的 MVC开发支持基于 Reactive 的 WEB 开发以及集成 JMS、JavaMail、JMX、缓存等其他模块3、IoC 容器3-1、初识IoC、 JavaBeanSpring 提供的容器又叫做 IoC容器。IoC全称Inversion of Control,直译为控制反转。为什么需要IoC?按照以往的开发的方式假设我们需要通过UserService来获取指定用户的信息:public class UserService { private HikariConfig config = new HikariConfig(); private DataSource dataSource = new HikariDataSource(config); public User getUserById(Long userId){ try(Connection conn = dataSource.getConnection()){ ... return user; } } }为了从数据库中查询出用户,UserService持有一个DataSource。为了实例化一个HikariDataSource,又需要实例化一个HikariConfig查询完用户,比如需要通过UserRoleService再去查询用户的角色信息:public class UserRoleService { private HikariConfig config = new HikariConfig(); private DataSource dataSource = new HikariDataSource(config); public User getUserRoleByUserId(Long userId){ try(Connection conn = dataSource.getConnection()){ ... return role; } } }因为UserRoleService中也需要去访问数据库,所以不得不又实例化了一个HikariDataSource现在我们处理用户发送过来的请求:public class UserInfoServlet extends HttpServlet { private UserService userService = new UserService(); private UserRoleService userRoleService = new UserRoleService(); protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { long userId = getFromCookie(req); User user = userService.getUserById(userId); Role role = userRoleService.getUserRoleByUserId(userId); .... } }上述的每一个Service组件都是以new的形式创建出来的,其有部分缺点:实例化一个组件比较复杂,例如UserService 和 UserRoleService 都需要创建HikariDataSource ,实际上需要读取配置,才能实例化HikariConfig,然后再去实例化HikariDataSource没有必要让每个Service组件分别去创建DataSource的实例,完全可以共享一个DataSource 实例,但是由谁去创建,谁去获取创建好的实例,都不好处理。同理UserInfoServlet或者其他的xxxServlet 需要使用UserService实例或者UserRoleService实例时,也应当共享同一个实例,但是比较复杂不好处理很多组件需要销毁以便释放资源,例如DataSource,但如果组件被对多个组件共享,怎样确保他的使用方已被全部销毁随着组件越来越多,需要共享的组件写起来非常困难,依赖关系也更加复杂从上面几个问题中,不难看出,随着系统越来越庞大,组件越来越多,其组件的生命周期和相互之间的依赖关系如果由组件自身来维护,则会大大增加系统的复杂度,也会导致组件之间耦合度越来越高,测试维护越来越困难因此,核心问题是:谁负责创建组件谁负责根据依赖关系组装组件销毁的时候,如何按照依赖顺序正确销毁解决这些问题的核心方案就是IoC容器在传统的项目中,控制权在程序本身,程序的控制流程完全由开发者控制,例如:UserInfoServlet 创建了UserService, 在创建UserService的时候又创建了DataSource 组件。这种模式的缺点就是,一个组件如果要使用另一个组件,就必须要先知道如何正确的创建它,否则无法正常使用在IoC模式下,控制权发生了反转,即从应用程序转移到了IoC容器,所以组件不再由应用程序自己创建和配置,而是由IoC容器负责。应用程序只需要直接使用已经创建好并且配置好的组件。为了能让组件在IoC容器中被“装配”出来,需要某种“注入”机制。例如UserService自己并不会去创建DataSource,而是等待外部通过setDataSource()方法来注入一个配置好的DataSource:public class UserService { private DataSource dataSource; public void setDataSource(DataSource dataSource){ this.dataSource = dataSource; } }不需要再通过new的形式来实例化一个DataSource组件,而是通过注入的方式注入一个DataSource,这种方式带来了很多好处:UserService不需要关心如何创建的DataSource,因此不需要再去写读取数据库配置之类的代码DataSource 被注入到 UserService 同样也可以被注入到 UserRoleService 中,共享一个组件变得非常简单因此IoC又称为依赖注入,它解决了一个最主要的问题:将组件的创建+配置与组件的使用分离开,并且由IoC容器负责管理组件的生命周期因为IoC容器要负责实例化所有的组件,因此需要告诉容器如何创建组件,以及之间的依赖关系。通过配置XML文件的来实现:<beans> <bean id="dataSource" class="HirkariDataSource" /> <bean id="userService" class="UserService"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="userRoleService" class="UserRoleService"> <property name="dataSource" ref="dataSource" /> </bean> </beans>上述XML配置文件指示IoC容器创建三个JavaBean组件,并把id为dataSource的组件通过属性dataSource(调用setDataSource()函数)注入到了另外两个组件中,在Spring IoC中,我们把所有的组件统称为JavaBean,即配置一个组件就是配置一个JavaBean依赖注入方式从上面可以看出,依赖注入可以通过set()方法实现。同时依赖注入也可以通过构造方法实现通过有参构造注入依赖:public class UserService { private DataSource dataSource; public UserService(DataSource dataSource) { this.dataSource = dataSource; } }无侵入容器 Spring的IoC容器是一个高度可拓展的无侵入容器。所谓无侵入就是指应用程序的组件无需实现Spring的特定接口,或者说组件根本不知道自己在Spring的容器中运行3-2、配置JavaBean的方式项目目录结构spring-ioc-appcontext ├── pom.xml └── src └── main ├── java │ └── com │ └── bystart │ └── learnjava │ ├── Main.java │ └── service │ ├── User.java │ ├── UserService.java │ └── UserRoleService.java └── resources └── application.xml3-2-1、使用XML配置application.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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userService" class="com.bystart.learnjava.service.UserService"/> <bean id="userRoleService" class="com.bystart.learnjava.service.UserRoleService" /> <!-- 注入一些属性案例 <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="password" /> <property name="maximumPoolSize" value="10" /> <property name="autoCommit" value="true" /> </bean> --> <!-- 注入一个Bean 案例 <bean id="userService" class="com.bystart.learnjava.service.UserService"> <property name="dataSource" ref="dataSource" /> </bean> --> </beans>Main.java 文件内容:public class Main { public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); UserService userService = context.getBean(UserService.class); UserRoleService userRoleService = context.getBean(UserRoleService.class); User loginUser = userService.login("username@qq.com","password"); loginUser.setRoleName(userRoleService.getUserRoleNameByUserId(loginUser.getUserId())); } }我们从创建Spring的代码中可以看出,Spring容器就是ApplicationContext,它是一个接口,有很多实现类, 这里我们选择ClassPathXmlApplicationContext表示它会自动从classpath中查找指定的xml配置文件ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");获得了ApplicationContext的实例,就获得了IoC容器的引用。从ApplicationContext中我们可以根据Bean的id获取Bean,但更多的时候一般根据Bean的类型来获取Bean的引用UserService userService = context.getBean(UserService.class);Spring还提供了另一种IoC容器叫BeanFactory,使用方式和ApplicationContext 类似:BeanFactory factory = new XmlBeanFactory(new ClassPathResource("application.xml")); UserService userService = factory.getBean(UserService.class);BeanFactory和ApplicationContext的区别在于,BeanFactory的实现是按需创建,即第一次获取Bean时才创建这个Bean,而ApplicationContext会一次性创建所有的Bean。实际上,ApplicationContext接口是从BeanFactory接口继承而来的,并且,ApplicationContext提供了一些额外的功能,包括国际化支持、事件和通知机制等。通常情况下,我们总是使用ApplicationContext,很少会考虑使用BeanFactory3-2-2、使用注解配置我们可以使用更简单的方式来配置一个JavaBean,那就是使用注解@Component:@Component public class UserRoleService { }@Component 注解就相当于定义了一个Bean,它有一个可选的名称,默认是userRoleService,即小写开头的类名想使用这个Bean也是非常的简单只需要加一个 @Autowired,自动注入到属性,想要注入对应的依赖,则自己也需要被IoC托管,需要加上@Component注解:@Component public class UserService { @Autowired private UserRoleService userRoleService; }@Autowired 还可以加入到构造方法中@Component public class UserService { private UserRoleService userRoleService; public UserService(@Autowired UserRoleService userRoleService){ this.userRoleService = userRoleService; } }开启注解扫描,我们在Main.java添加部分代码:@Configuration @ComponentScan public class Main { public static void main(String[] args){ ApplicationContext context = new AnnotationConfigApplicationContext(Main.class); UserService userService = context.getBean(UserService.class); UserRoleService userRoleService = context.getBean(UserRoleService.class); User loginUser = userService.login("username@qq.com","password"); loginUser.setRoleName(userRoleService.getUserRoleNameByUserId(loginUser.getUserId())); } }Main.class中加入一个注解@Configuration,表示他是一个配置类,因为在创建ApplicationContext时:ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);使用的实现类是AnnotationConfigApplicationContext,他的构造方法必须传入一个标注了@Configuration 的类,此外Main.java还标注了@ComponentScan它表示告诉容器,自动搜索扫描当前类所在的包及子包下所有标注了 @Component的类给创建到IoC容器中进行托管,并根据@Autowired 自动装配使用Annotation配合自动扫描能大幅简化Spring的配置,我们只需要保证:每个Bean被标注为@Component并正确使用@Autowired注入配置类被标注为@Configuration和@ComponentScan所有Bean均在指定包以及子包内4、Spring AOP在AOP编程中,我们经常会遇到下面的概念:Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点Joinpoint:连接点,即定义在应用程序流程的何处插入切面的执行Pointcut:切入点,即一组连接点的集合Advice:增强,指特定连接点上执行的动作Introduction:引介,指为一个已有的Java对象动态地增加新的接口Weaving:织入,指将切面整合到程序的执行流程中Interceptor:拦截器,是一种实现增强的方式Target Object:目标对象,即真正执行业务的核心逻辑对象AOP Proxy:AOP代理,是客户端持有的增强后的对象引用4-1、装配AOP我们以UserService 和 UserRoleService 为例,我们给UserService的每个业务的方法执行前添加日志,给UserRoleService每个方法执行前后添加日志记录我们定义一个LoggingAspect:@Aspect @Component public class LoggingAspect { // 在 UserService 的每个方法执行前执行该部分代码 @Before("execution(public * com.bystart.learnjava.service.UserService.*(..))") public void doAddUserServiceLog(){ System.out.println("开始添加日志"); ... } // 在 UserRoleService 的每个方法 执行前/后 执行该部分代码 @Around("execution(public * com.bystart.learnjava.service.UserRoleService.*(..))") public Object doAddUserRoleServiceLog(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("开始添加执行前日志"); // 获取目标方法的返回内容 Object retVal = pjp.proceed(); System.err.println("开始添加执行后的日志,目标方法返回内容:" + pjp.getSignature()); // 真正返回内容给调用方 return retVal; } }观察doAddUserServiceLog()方法,我们定义了一个@Before注解,后面的字符串是告诉AspectJ应该在何处执行该方法,这里写的意思是:执行UserService的每个public方法前执行doAddUserServiceLog()代码再观察doAddUserRoleServiceLog()方法,我们定义了一个@Around注解,它和@Before不同,@Around可以决定是否执行目标方法,因此,我们在doAddUserRoleServiceLog()内部先打印日志,再调用方法,最后打印日志后返回结果在LoggingAspect类的声明处,除了用@Component表示它本身也是一个Bean外,我们再加上@Aspect注解,表示它的@Before标注的方法需要注入到UserService的每个public方法执行前,@Around标注的方法需要注入到UserRoleService的每个public方法执行前后紧接着,我们需要给@Configuration类加上一个@EnableAspectJAutoProxy注解:@Configuration @ComponentScan @EnableAspectJAutoProxy public class Main { public static void main(String[] args){ ApplicationContext context = new AnnotationConfigApplicationContext(Main.class); UserService userService = context.getBean(UserService.class); UserRoleService userRoleService = context.getBean(UserRoleService.class); User loginUser = userService.login("username@qq.com","password"); loginUser.setRoleName(userRoleService.getUserRoleNameByUserId(loginUser.getUserId())); } }Spring的IoC容器看到这个注解,就会自动查找带有@Aspect的Bean,然后根据每个方法的@Before、@Around等注解把AOP注入到特定的Bean中那么LoggingAspect是怎么实现将方法注入到其他Bean,并且在目标方法执行前后触发LoggingAspect的方法的呢其实原理非常的简单,它是创建了一个代理对象:public class UserServiceAopProxy extends UserService { private UserService target; private LoggingAspect aspect; public UserServiceAopProxy(UserService target, LoggingAspect aspect){ this.target = target; this.aspect = aspect; } public User login(String email, String password){ // 先执行Aspect的代码 aspect.doAddUserServiceLog(); // 再执行原有的逻辑 return target.login(email, password); } }这些都是Spring容器启动时为我们自动创建的注入了Aspect的子类,它取代了原始的UserService(原始的UserService实例作为内部变量隐藏在UserServiceAopProxy中)。如果我们打印从Spring容器获取的UserService实例类型,它类似UserService$$EnhancerBySpringCGLIB$$1f44e01c,实际上是Spring使用CGLIB动态创建的子类,但对于调用方来说,感觉不到任何区别。Spring对接口类型使用JDK动态代理,对普通类使用CGLIB创建子类。如果一个Bean的class是final,Spring将无法为其创建子类。可见,虽然Spring容器内部实现AOP的逻辑比较复杂(需要使用AspectJ解析注解,并通过CGLIB实现代理类),但我们使用AOP非常简单,一共需要三步:定义执行方法,并在方法上通过AspectJ的注解告诉Spring应该在何处调用此方法标记@Component和@Aspect在@Configuration类上标注@EnableAspectJAutoProxy4-2、拦截器类型顾名思义,拦截器有以下类型:@Before:这种拦截器先执行拦截代码,再执行目标代码。如果拦截器抛异常,那么目标代码就不执行了@After:这种拦截器先执行目标代码,再执行拦截器代码。无论目标代码是否抛异常,拦截器代码都会执行@AfterReturning:和@After不同的是,只有当目标代码正常返回时,才执行拦截器代码@AfterThrowing:和@After不同的是,只有当目标代码抛出了异常时,才执行拦截器代码@Around:能完全控制目标代码是否执行,并可以在执行前后、抛异常后执行任意拦截代码,可以说是包含了上面所有功能
2023年07月03日
80 阅读
0 评论
2 点赞