Как написать свою java библиотеку

Introduction

If you usually need to rewrite or copy similar code between different projects, it may be time to stop replicating it and create a library.

You can also share it as open-source, so other people can use it and help you improve it.

To use your library in different projects, you have to publish it on a repository like Maven Central Repository.

So let’s run through the entire process and publish a library for padding Strings. We’ll start by creating our project from scratch.


NOTE

If you want to skip the project creation:

  • You can use your own project and jump ahead to Preparing pom.xml to Deploy on Maven Central; or

  • Download my project from GitHub and jump ahead to Requesting Access to Maven Central.

    ⚠️ IF YOU USE MY PROJECT, DON’T FORGET TO CHANGE ITS GROUP ID.


Creating the Project

Run the following command on your terminal:

mvn archetype:generate -DgroupId=com.thegreatapi.demolibrary -DartifactId=demolibrary -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false

NOTE

Use your own group ID on the command. If you use com.thegreat.api.demolibrary you won’t be able to publish to Maven Central.

If you’re not sure about what group ID to use, look at this article.


That command will create a project with the following pom.xml:

<?xml version="1.0" encoding="UTF-8"?>

<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.0</modelVersion>

    <groupId>com.thegreatapi.demolibrary</groupId>
    <artifactId>demolibrary</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>demolibrary</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
                <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
                <plugin>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>3.7.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-project-info-reports-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

Let’s change it to use Java 11 instead of 1.7.

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

And then we create a LICENSE file. I’ll use Apache 2 License, but you can use any license you want. To use Apache 2 License, you have to copy the content from http://www.apache.org/licenses/LICENSE-2.0.txt and paste it into your LICENSE file.

Implementing the Library

Now let’s create an interface StringPadder in the package com.thegreatapi.demolibrary.stringpadder. That will be the interface that clients will use to pad Strings.

package com.thegreatapi.demolibrary.stringpadder;

/**
 * Pads a {@link String}.
 * <p>
 * The instances of classes that implement this interface are thread-safe and immutable.
 */
public interface StringPadder {

    /**
     * Returns a new {@link String} that right-aligns the characters in the specified String by padding them with spaces
     * on the left, for a specified total length.
     *
     * @param stringToPad the {@link String} to be padded
     * @param totalLength total length of the new {@link String}
     * @return the padded {@link String}
     */
    String padLeft(String stringToPad, int totalLength);

    /**
     * Returns a new {@link String} that right-aligns the characters in the specified String by padding them with the
     * specified char on the left, for a specified total length.
     *
     * @param stringToPad the {@link String} to be padded
     * @param totalLength total length of the new {@link String}
     * @return the padded {@link String}
     */
    String padLeft(String stringToPad, int totalLength, char paddingCharacter);

    /**
     * Returns a new {@link String} that left-aligns the characters in the specified String by padding them with spaces
     * on the left, for a specified total length.
     *
     * @param stringToPad the {@link String} to be padded
     * @param totalLength total length of the new {@link String}
     * @return the padded {@link String}
     */
    String padRight(String stringToPad, int totalLength);

    /**
     * Returns a new {@link String} that left-aligns the characters in the specified String by padding them with the
     * specified char on the left, for a specified total length.
     *
     * @param stringToPad the {@link String} to be padded
     * @param totalLength total length of the new {@link String}
     * @return the padded {@link String}
     */
    String padRight(String stringToPad, int totalLength, char paddingCharacter);

}

Now let’s create the implementation of the StringPadder interface.

package com.thegreatapi.demolibrary.stringpadder;

class StringPadderImpl implements StringPadder {

    StringPadderImpl() {
    }

    @Override
    public String padLeft(String stringToPad, int totalLength) {
        return padLeft(stringToPad, totalLength, ' ');
    }

    @Override
    public String padLeft(String stringToPad, int totalLength, char paddingCharacter) {
        return getStringToBeAdded(stringToPad, totalLength, paddingCharacter) + stringToPad;
    }

    @Override
    public String padRight(String stringToPad, int totalLength) {
        return padRight(stringToPad, totalLength, ' ');
    }

    @Override
    public String padRight(String stringToPad, int totalLength, char paddingCharacter) {
        return stringToPad + getStringToBeAdded(stringToPad, totalLength, paddingCharacter);
    }

    private String getStringToBeAdded(String stringToPad, int totalLength, char paddingCharacter) {
        int quantity = totalLength - stringToPad.length();

        if (quantity > 0) {
            return Character.toString(paddingCharacter).repeat(quantity);
        } else {
            return "";
        }
    }

}

Note that the class is package-private, so the clients can’t use it in their code.

Now we’re going to create a factory for clients to create instances of StringPadder.

package com.thegreatapi.demolibrary.stringpadder;

/**
 * Factory for creating instances of {@link StringPadder}.
 */
public final class StringPadderFactory {

    private StringPadderFactory() {
    }

    /**
     * Creates an instance of {@link StringPadder}.
     *
     * @return the new instance
     */
    public static StringPadder createStringPadder() {
        return new StringPadderImpl();
    }

}

Creating Tests

Let’s replace JUnit 4 with JUnit 5 and add AssertJ dependency to our pom.xml.

<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.7.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>3.19.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Now we’re ready to implement our tests.

package com.thegreatapi.demolibrary.stringpadder;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class StringPadderImplTest {

    private final StringPadderImpl stringPadder = new StringPadderImpl();

    @Test
    void padLeft() {
        assertThat(stringPadder.padLeft("thegreatapi.com", 20))
                .isEqualTo("     thegreatapi.com");
    }

    @Test
    void padLeftWithZeros() {
        assertThat(stringPadder.padLeft("thegreatapi.com", 20, '0'))
                .isEqualTo("00000thegreatapi.com");
    }

    @Test
    void padRight() {
        assertThat(stringPadder.padRight("thegreatapi.com", 20))
                .isEqualTo("thegreatapi.com     ");
    }

    @Test
    void padRightWithZeros() {
        assertThat(stringPadder.padRight("thegreatapi.com", 20, '0'))
                .isEqualTo("thegreatapi.com00000");
    }

    @Test
    void padLeftWithInvalidTotalLength() {
        assertThat(stringPadder.padLeft("thegreatapi.com", 3))
                .isEqualTo("thegreatapi.com");
    }

    @Test
    void padLeftWithZerosInvalidTotalLength() {
        assertThat(stringPadder.padLeft("thegreatapi.com", 3, '0'))
                .isEqualTo("thegreatapi.com");
    }

    @Test
    void padRightInvalidTotalLength() {
        assertThat(stringPadder.padRight("thegreatapi.com", 3))
                .isEqualTo("thegreatapi.com");
    }

    @Test
    void padRightWithZerosInvalidTotalLength() {
        assertThat(stringPadder.padRight("thegreatapi.com", 3, '0'))
                .isEqualTo("thegreatapi.com");
    }

}

If we run mvn verify, we should see an output similar to the following:

/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home/bin/java -Dmaven.multiModuleProjectDirectory=/Users/helber/Desktop/demolibrary -Dmaven.home=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3 -Dclassworlds.conf=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/bin/m2.conf -Dmaven.ext.class.path=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven-event-listener.jar -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=61185:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds.license:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds-2.6.0.jar org.codehaus.classworlds.Launcher -Didea.version=2020.3.3 verify
[INFO] Scanning for projects...
[INFO] 
[INFO] --------------< com.thegreatapi.demolibrary:demolibrary >---------------
[INFO] Building demolibrary 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ demolibrary ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/helber/Desktop/demolibrary/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ demolibrary ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /Users/helber/Desktop/demolibrary/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ demolibrary ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/helber/Desktop/demolibrary/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ demolibrary ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/helber/Desktop/demolibrary/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ demolibrary ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.thegreatapi.demolibrary.stringpadder.StringPadderImplTest
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.07 s - in com.thegreatapi.demolibrary.stringpadder.StringPadderImplTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ demolibrary ---
[INFO] Building jar: /Users/helber/Desktop/demolibrary/target/demolibrary-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.378 s
[INFO] Finished at: 2021-04-05T06:36:12-03:00
[INFO] ------------------------------------------------------------------------

Process finished with exit code 0

Our implementation is ready. The next step is to deploy our library to Maven Central.

Preparing pom.xml to Deploy on Maven Central

There are some requirements we need to satisfy in order to upload our library to Maven Central. We can find those requirements on https://central.sonatype.org/pages/requirements.html.

The first thing we have to change in our pom.xml is to define a non-SNAPSHOT version for our library. To do that, we just need to remove the -SNAPSHOT suffix. Let’s define our version as 0.0.1.

<version>0.0.1</version>

Next, we have to add a description and a URL to our project. In my case, the URL should be http://thegreatapi.com. Your URL must differ from mine because you have to own the domain. You can also use a GitHub URL if you prefer.

<description>
    This project is a library for padding Strings in Java.
    DON'T USE THIS IN PRODUCTION. IT WAS CREATED FOR DEMO PURPOSES ONLY.
</description>
<url>http://thegreatapi.com</url>

After that, we add the license information. Note that I’m using Apache 2 license. Use the same license that you defined in your LICENSE file.

<licenses>
    <license>
        <name>The Apache License, Version 2.0</name>
        <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
    </license>
</licenses>

The next step is to add information about the developers.

<developers>
    <developer>
        <name>Helber Belmiro</name>
        <email>your@email.com</email>
        <organization>The Great API</organization>
        <organizationUrl>https://thegreatapi.com</organizationUrl>
    </developer>
</developers>

And then, add the information about the Source Code Management (SCM). The following information is for my project. Replace it with your project’s information.

<scm>
    <connection>scm:git:git@github.com:hbelmiro/demo-library.git</connection>
    <developerConnection>scm:git:ssh://github.com:hbelmiro/demo-library.git</developerConnection>
    <url>https://github.com/hbelmiro/demo-library/tree/master</url>
</scm>

Maven Central demands you to send the Javadoc and source code. So you have to create it when building your artifacts. You must also sign the artifacts you are sending to Maven Central.

Since we need to do it only when deploying to Maven Central, it might be a good idea to create a profile for this.

<profiles>
    <profile>
        <id>release</id>
        <activation>
            <property>
                <name>performRelease</name>
                <value>true</value>
            </property>
        </activation>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-gpg-plugin</artifactId>
                    <version>1.6</version>
                    <executions>
                        <execution>
                            <id>sign-artifacts</id>
                            <phase>verify</phase>
                            <goals>
                                <goal>sign</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-javadoc-plugin</artifactId>
                    <version>3.2.0</version>
                    <executions>
                        <execution>
                            <id>attach-javadocs</id>
                            <goals>
                                <goal>jar</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <version>3.2.1</version>
                    <executions>
                        <execution>
                            <id>attach-sources</id>
                            <goals>
                                <goal>jar-no-fork</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>
<distributionManagement>
    <snapshotRepository>
        <id>ossrh</id>
        <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
    </snapshotRepository>
    <repository>
        <id>ossrh</id>
        <url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    </repository>
</distributionManagement>

Also, add nexus-staging-maven-plugin.

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.sonatype.plugins</groupId>
                <artifactId>nexus-staging-maven-plugin</artifactId>
                <version>1.6.7</version>
                <extensions>true</extensions>
                <configuration>
                    <serverId>ossrh</serverId>
                    <nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
                    <autoReleaseAfterClose>true</autoReleaseAfterClose>
                </configuration>
            </plugin>
            ...
        </plugins>
    </pluginManagement>
<build>

Your library is ready to be published to Maven Central.

Requesting Access to Maven Central

Using the OSS Repository Hosting (OSSRH), provided by Sonatype for any open-source project, is the easiest way to publish your project.

The first step is to create a JIRA account.

After that, you have to create a New Project ticket. As an example, you can use the ticket I opened to publish demolibrary.

Officially, they ask you for 2 working days to complete the process, but it normally takes one or two hours.


NOTE

Use your own group ID. If you use com.thegreat.api.demolibrary you won’t be able to publish to Maven Central.


They will ask if you own the domain specified on groupId and if so, you’ll have to verify the ownership. On my ticket they commented:

Comment posted by Central OSSRH about the domain ownership.

In my case, I added a TXT record to my DNS. When I did that, I commented on the ticket, and they set the ticket to «Resolved».

Releasing the Library

After proving the domain ownership, you’re ready to upload your artifacts. To do that, you first release it to staging.

mvn clean deploy -P release

At this point, your artifacts are stored in a private repository, so you can verify them before releasing them. So log in to https://s01.oss.sonatype.org/ using your JIRA credentials.

On the left menu, click on «Staging Repositories» and you’ll see your library.

If everything is OK with your library, you can close it, otherwise, you can drop it.

When you close it, on the low section, you can check if it was closed successfully by clicking on the «Activity» tab. You should see something similar to this:

Repository closed

If the repository was successfully closed, now you’re able to promote it to release.

Run the following command using your Repository ID. On the image above, it’s comthegreatapidemolibrary-1005. Don’t forget to replace it with your own ID.

mvn nexus-staging:release -DstagingRepositoryId=comthegreatapidemolibrary-1005


NOTE

Use your own group ID. If you use com.thegreat.api.demolibrary you won’t be able to publish to Maven Central.


You should see an output similar to:

[INFO]  * Connected to Nexus at https://s01.oss.sonatype.org:443/, is version 2.14.20-02 and edition "Professional"
[INFO] Releasing staging repository with IDs=[comthegreatapidemolibrary-1005]

Waiting for operation to complete...
.........

[INFO] Released
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  28.583 s
[INFO] Finished at: 2021-04-07T07:53:50-03:00
[INFO] ------------------------------------------------------------------------

Now your library is published. On this very first deployment, you have to comment on the JIRA ticket, so they can activate the sync to Maven Central.

After Sonatype activates Maven Central sync for your library, when you successfully release new components, they will be published to Central https://repo1.maven.org/maven2/, typically within 10 minutes, though updates to https://search.maven.org can take up to two hours.

Start Your Library Now

You are now ready to start your own library. It will be great for you and the Java community.

To help you with that, I wrote an article about the principles that sustain great frameworks and libraries.

If you still don’t feel prepared, what’s preventing you from taking the next step? Comment here or send me a message on my social media. I’ll be happy to help you.

I created a program in Java and I designed it so that methods that I want them to appear (getter methods) in the main, I can call them easily after initiate the class that holds these methods.

The question is that, I need to make this application (that holds the getter methods) to be like an API so that I can give my application for developers to use my functions (the getter methods) if they need them, and only what they need is to add this file (I think the API after is done shown as .jar file).

How can I make it so that I can make my code reusable with other application? It’s similar to the .dll, I think.

Thanks a lot ;)

naXa stands with Ukraine's user avatar

asked Aug 31, 2010 at 19:39

Q8Y's user avatar

2

Create a JAR. Then include the JAR. Any classes in that JAR will be available. Just make sure you protect your code if you are giving out an API. Don’t expose any methods / properties to the end user that shouldn’t be used.

Edit: In response to your comment, make sure you don’t include the source when you package the JAR. Only include the class files. That’s the best you can really do.

answered Aug 31, 2010 at 19:42

Zack Marrapese's user avatar

Zack MarrapeseZack Marrapese

12.1k9 gold badges51 silver badges69 bronze badges

9

To be useable as an API, your classes should:

  • Use a unique package (ideally following the convention, i.e. the reverse of a domain you own as prefix). This prevents naming conflicts
  • Have only those classes and methods public or protected that are intended to be used by others. This makes it easier to use.
  • Have extensive Javadoc comments.
  • Be available as a JAR file — ideally separate JARs for binary distribution, source code and javadoc files.

answered Aug 31, 2010 at 19:49

Michael Borgwardt's user avatar

Michael BorgwardtMichael Borgwardt

340k77 gold badges478 silver badges713 bronze badges

You need to package your application as a jar file. You can use ant jar task to create jar files or you can use the jar command.

For ant tasks look at this link.

For creating it manually look at this link.

answered Aug 31, 2010 at 19:43

CoolBeans's user avatar

CoolBeansCoolBeans

20.5k10 gold badges83 silver badges100 bronze badges

4

Make sure you write and publish javadocs for all your public and protected classes and methods.

answered Aug 31, 2010 at 19:44

Noel M's user avatar

Noel MNoel M

15.6k8 gold badges38 silver badges46 bronze badges

3

To create the jar:

jar cf <jar_name> <sources>

answered Jul 31, 2012 at 22:35

jmoreira's user avatar

jmoreirajmoreira

1,5462 gold badges16 silver badges26 bronze badges

There are several ways you can expose your code. Creating a jar and distributing that may be the easiest as other developers will just have to include your jar. However, if you are talking about «anyone» accessing your code, a web service may make more sense as you can provide access to the data without providing all of the necessary code. You mention providing access to your getters — if you just create a class that has getters, the other developers can use them, but how are they going to be populated? If your application is self contained in that it gets the necessary data and provides the getters, that should work, but if you are talking about providing access to data from your running application, a web service makes more sense as your application can retrieve the data and provide access via publicly accessible methods.

You most likely want to create interfaces as well so developers can code against the interface and you can change the internal workings without impacting them. Any API that will be used by others should be extensively documented as well.

answered Aug 31, 2010 at 19:51

Tai Squared's user avatar

Tai SquaredTai Squared

12.3k24 gold badges72 silver badges82 bronze badges

Well, depends on your IDE. I use Netbeans, so I just hit build project, and viola! A jar file is created in my directory specified. Now, that’s just for compiling. All anyone has to do is download your .jar file, and if in Netbeans, right click libraries, add jar/folder, and select the downloaded file.

answered Aug 21, 2015 at 22:05

gigy's user avatar

You can also consider:

  • Include some samples that demonstrate how to use your library
  • Build your jar using Apache Maven
  • Put your jar in a public maven repository
  • Publish a new version of your library as you find/fix bugs
  • If you want to hide your implementation, you can pack your jar with obfuscation, so that if someone decompiles your classes, the code will be difficult to read

answered Mar 1, 2018 at 13:23

elyor's user avatar

elyorelyor

9589 silver badges19 bronze badges

Создание библиотеки в стиле Spring Data Repository своими руками при помощи Dynamic Proxy и Spring IoC

А что если бы можно было создать интерфейс, например, такой:

А затем просто внедрять его и вызывать его методы:

Такое вполне возможно реализовать (и не очень то и сложно). Дальше я покажу, как и зачем это делать.

Недавно у меня была задача упростить для разработчиков взаимодействие с одним из используемых фреймворков. Нужно было дать им ещё более простой и удобный способ работать с ним, чем тот, который уже был реализован.

Свойства, которых хотелось добиться от такого решения:

  • декларативное описание желаемого действия
  • минимальное необходимое количество кода
  • интеграция с используемым фреймворком внедрения зависимостей (в нашем случае Spring)

Подобное реализовано в библиотеках Spring Data Repository и Retrofit. В них пользователь описывает желаемое взаимодействие в виде java интерфейса, дополненного аннотациями. Пользователю не нужно самому писать реализацию — её генерирует библиотека в рантайме на основе сигнатур методов, аннотаций и типов.

Когда я изучал тему, у меня возникало много вопросов, ответы на которые были разбросаны по всему интернету. Мне бы в тот момент не помешала статья, подобная этой. Потому здесь я постарался собрать всю информацию и мой опыт в одном месте.

В данном посте я покажу, как можно реализовать данную идею, на примере обёртки для http-клиента. Пример игрушечный, предназначенный не для реального использования, а для демонстрации подхода. Исходники проекта можно изучить на bitbucket.

Как это выглядит для пользователя

Пользователь описывает необходимый ему сервис в виде интерфейса. Например, для выполнения http запросов в google:

Что в конечном итоге будет делать реализация данного интерфейса, определяется по сигнатуре. Если возвращаемый тип int — будет выполняться http запрос и возвращаться статус код результата. Если возвращаемый тип CloseableHttpResponse, то возвращаться будет ответ на запрос целиком, и так далее. Куда будет делаться запрос — будем брать из аннотации Uri, подставляя в её содержимое вместо плейсхолдеров одноимённые переданные значения.

В данном примере я ограничился поддержкой трёх возвращаемых типов и одной аннотации. Также можно использовать для выбора реализации имена методов, типы параметров, использовать всевозможные их комбинации, но эту тему я вскрывать в данном посте не буду.

Когда пользователь хочет использовать данный интерфейс, он внедряет его в свой код используя Spring:

Интеграция со Spring нужна была в моём рабочем проекте, но она, разумеется, не единственно возможная. Если вы не используете внедрение зависимостей, получение реализации можно сделать, например, через static factory method. Но я в данной статье буду рассматривать именно Spring.

Данный подход очень удобен: достаточно пометить свой интерфейс как компонент Spring (аннотация Service в данном случае), и он готов к внедрению и использованию.

Как заставить Spring поддерживать эту магию

Типичное Spring приложение сканирует classpath на старте и ищет все компоненты, помеченные специальными аннотациями. Для них оно регистрирует BeanDefinition’ы — рецепты, по которым будут создаваться данные компоненты. Но если в случае конкретных классов Spring знает, как их создать, какие вызвать конструкторы и что в них передать, то для абстрактных классов и интерфейсов у него такой информации нет. Поэтому для нашего GoogleSearchApi Spring не будет создавать BeanDefinition. В этом ему потребуется помощь от нас.

Для того, чтобы допилить логику обработку BeanDefinition’ов, в спринге существует интерфейс BeanDefinitionRegistryPostProcessor. С помощью него мы можем добавить в BeanDefinitionRegistry какие нам угодно определения бинов.

К сожалению, я не нашёл способа встроиться в логику Spring сканирования classpath, чтобы обработать и обычные бины и наши интерфейсы за один проход. Поэтому я создал и использовал наследника класса ClassPathScanningCandidateComponentProvider для того, чтобы найти все интерфейсы, помеченные аннотацией Service:

Полный код сканирования пакетов и регистрации BeanDefinition’ов:

Готово! На старте приложения Spring выполнит данный код и зарегистрирует все необходимые интерфейсы, как бины.

Создание реализации найденных бинов делегируется отдельному компоненту DynamicProxyBeanFactory:

Для создания реализации используется старый добрый механизм Dynamic Proxy. Реализация создаётся на лету при помощи метода Proxy.newProxyInstance. О нём уже много написано статей, поэтому останавливаться здесь подробно я не буду.

Поиск нужного обработчика и обработка вызова

Как можно увидеть, DynamicProxyBeanFactory перенаправляет обработку метода в DynamicProxyInvocationHandlerDispatcher. Так как у нас существует потенциально много реализаций обработчиков (на каждую аннотацию, на каждый возвращаемый тип, и т.д.), то логично завести какое-то центральное место их хранения и поиска.

Для того, чтобы определять, подходит ли обработчик для обработки вызванного метода, я расширил стандартный интерфейс InvocationHandler новым методом

В результате получился интерфейс ProxyInvocationHandler, реализации которого и будут нашими обработчиками. Также реализации обработчиков будут помечены как Component, чтобы Spring мог соберать их для нас в один большой список внутри DynamicProxyInvocationHandlerDispatcher:

В методе findHandler мы проходимся по всем обработчикам и возвращем первый попавшийся, способный обработать переданный метод. Данный механизм поиска может быть не очень эффективен, когда реализаций обработчиков станет много. Возможно, тогда нужно будет задуматься о какой-то более подходящей структуре для их хранения, чем список.

Реализация обработчиков

В задачи обработчиков входит считывание информации о вызванном методе интерфейса и обработка самого вызова.

Что должен сделать обработчик в данном случае:

  1. Считать аннотацию Uri, достать её содержимое
  2. Заменить в строке Uri плейсхолдеры на реальные значения
  3. Считать возвращаемый тип метода
  4. Если возвращаемый тип подходит, выполнить обработку метода и вернуть результат.

Первые три пункта нужны для всех возвращаемых типов, поэтому общий код я вынес в абстрактный суперкласс
HttpInvocationHandler:

Во вспомогательном классе UriHandler реализована работа с аннотацией Uri: считывание значения, замена плейсхолдеров. Код его я тут приводить не буду, т.к. он довольно утилитный.
Но стоит отметить, что для считывания имён параметров из сигнатуры метода java, нужно при компиляции добавить опцию «-parameters».
HttpClient — обёртка над апачевским CloseableHttpClient, является бэкэндом для данной библиотеки.

В качестве примера конкретного обработчика приведу обработчик, возвращающий статус код ответа:

Остальные обработчики сделаны аналогично. Добавление новых обработчиков выполняется просто и не требует модификации существующего кода — просто создаём новый обработчик и помечаем его как компонент Spring.

Вот и всё. Код написан и готов к работе.

Заключение

Чем больше я думаю о подобном дизайне, тем больше вижу в нём недостатков. Слабые стороны, которые я вижу:

  • Type Safety, которой нет. Неправильно поставил аннотацию — до встречи с RuntimeException. Использовал неправильную комбинацию возвращаемого типа и аннотации — то же самое.
  • Слабая поддержка от IDE. Отсутствие автодополнения. Пользователь не можжет посмотреть, какие действия доступны ему в его ситуации (как если бы он поставил «точку» после объекта и увидел список доступных методов)
  • Мало возможностей для применения. Мне приходят на ум уже упомянутые http клиент, и клиент к базе данных. Но для чего ещё это можно применить?

Впрочем, у меня в рабочем проекте подход прижился и пользуется популярностью. Достоинства, которые я уже упоминал — простота, малое колчичество кода, декларативность, позволяют разработчикам концентрироваться на написании более важного кода.

А что вы думаете про такой подход? Стоит ли оно стараний? Какие вы видите в данном подходе проблемы? Пока я его всё ещё стараюсь осмыслить, пока он обкатывается в нашем продакшене, хотелось бы услышать что думают о нём другие люди. Надеюсь, данный материал был полезен кому-то.

Как создать библиотеку Java: С нуля до Maven Central

Как создать библиотеку Java: С нуля до введения в Maven Central Если ты… С тегами java, maven, библиотека, учебник.

  • Автор записи

Вступление

Если вам обычно нужно переписать или скопировать похожий код между разными проектами, возможно, пришло время прекратить его копирование и создать библиотеку.

Вы также можете поделиться им с открытым исходным кодом, чтобы другие люди могли использовать его и помочь вам улучшить его.

Чтобы использовать вашу библиотеку в разных проектах, вы должны опубликовать ее в репозитории, например Центральное хранилище Maven .

Итак, давайте пройдем весь процесс и опубликуем библиотеку для заполнения строк. Мы начнем с создания нашего проекта с нуля.

Если вы хотите пропустить создание проекта:

    Вы можете использовать свой собственный проект и перейти к Подготовка pom.xml для развертывания на Maven Central ; или

Загрузите мой проект с GitHub и перейдите к Запрашиваю доступ к Maven Central .

⚠️ ЕСЛИ ВЫ ИСПОЛЬЗУЕТЕ МОЙ ПРОЕКТ, НЕ ЗАБУДЬТЕ ИЗМЕНИТЬ ИДЕНТИФИКАТОР ЕГО ГРУППЫ.

Создание проекта

Выполните следующую команду на своем терминале:

Используйте свой собственный идентификатор группы в команде. Если вы используете библиотеку com.the great.api.demo вы не сможете опубликовать в Maven Central.

Если вы не уверены, какой идентификатор группы использовать, посмотрите эту статью .

Эта команда создаст проект со следующими pom.xml :

Давайте изменим его, чтобы использовать Java 11 вместо 1.7.

А затем мы создаем файл ЛИЦЕНЗИЯ . Я буду использовать лицензию Apache 2, но вы можете использовать любую лицензию, какую захотите. Чтобы использовать лицензию Apache 2, вам необходимо скопировать содержимое из http://www.apache.org/licenses/LICENSE-2.0.txt и вставьте его в свой ЛИЦЕНЗИОННЫЙ файл.

Реализация библиотеки

Теперь давайте создадим интерфейс Заполнитель строк в упаковке com.великий api.демонстрационная библиотека.заполнитель строк . Это будет интерфейс, который клиенты будут использовать для заполнения строк.

Теперь давайте создадим реализацию интерфейса String Padder .

Обратите внимание, что класс является закрытым для пакетов, поэтому клиенты не могут использовать его в своем коде.

Теперь мы собираемся создать фабрику для клиентов, чтобы создавать экземпляры String Padder .

Создание Тестов

Давайте заменим JUnit 4 на JUnit 5 и добавим зависимость AssertJ в ваш pom.xml .

Теперь мы готовы провести наши тесты.

Если мы запустим mvn verify , мы увидим вывод, подобный следующему:

Наша реализация готова. Следующим шагом будет развертывание нашей библиотеки в Maven Central.

Подготовка pom.xml для развертывания в Maven Central

Есть некоторые требования, которые мы должны выполнить, чтобы загрузить нашу библиотеку в Maven Central. Мы можем найти эти требования на https://central.sonatype.org/pages/requirements.html .

Первое, что мы должны изменить в вашем pom.xml заключается в определении версии без МОМЕНТАЛЬНОГО СНИМКА для нашей библиотеки. Для этого нам просто нужно удалить суффикс -SNAPSHOT . Давайте определим нашу версию как 0.0.1 .

Затем мы должны добавить описание и URL-адрес в наш проект. В моем случае URL-адрес должен быть http://thegreatapi.com . Ваш URL-адрес должен отличаться от моего, потому что вы должны владеть доменом. Вы также можете использовать URL-адрес GitHub, если хотите.

После этого мы добавляем информацию о лицензии. Обратите внимание, что я использую лицензию Apache 2. Используйте ту же лицензию, которую вы определили в своем файле ЛИЦЕНЗИЯ .

Следующий шаг – добавить информацию о разработчиках.

А затем добавьте информацию об управлении исходным кодом (SCM). Следующая информация предназначена для моего проекта. Замените его информацией о вашем проекте.

Maven Central требует, чтобы вы отправили Javadoc и исходный код. Поэтому вы должны создать его при создании своих артефактов. Вы также должны подписать артефакты, которые отправляете в Maven Central.

Поскольку нам нужно делать это только при развертывании в Maven Central, было бы неплохо создать профиль для этого.

Вам также необходимо установить клиент GPG и поместить его в путь командной строки. Следуйте инструкциям на https://central.sonatype.org/pages/working-with-pgp-signatures.html для установки клиента GPG.

Следующий шаг – добавить управление распространением в ваш pom.xml .

Кроме того, добавьте nexus-staging-maven-плагин.

Ваша библиотека готова к публикации в Maven Central.

Запрашиваю доступ к Maven Central

Использование хостинга репозитория OSS (OSSRH), предоставляемого Sonatype для любого проекта с открытым исходным кодом, является самым простым способом публикации вашего проекта.

После этого вам необходимо создать новый билет проекта . В качестве примера вы можете использовать билет, который я открыл для публикации демонстрационной библиотеки.

Официально они просят у вас 2 рабочих дня для завершения процесса, но обычно это занимает один или два часа.

Используйте свой собственный идентификатор группы. Если вы используете библиотеку com.the great.api.demo вы не сможете опубликовать в Maven Central.

Они спросят, являетесь ли вы владельцем домена, указанного в groupId, и если да, вам придется подтвердить право собственности. На моем билете они прокомментировали:

В моем случае я добавил текстовую запись в свой DNS. Когда я это сделал, я прокомментировал билет, и они установили для билета значение “Решено”.

Освобождение библиотеки

После подтверждения права собственности на домен вы готовы загрузить свои артефакты. Для этого вы сначала выпускаете его на стадию.

выпуск mvn clean deploy-P для чистого развертывания

На данный момент ваши артефакты хранятся в частном хранилище, поэтому вы можете проверить их перед выпуском. Так что войдите в https://s01.oss.sonatype.org/ используя ваши учетные данные JIRA.

В меню слева нажмите “Промежуточные хранилища”, и вы увидите свою библиотеку.

Если с вашей библиотекой все в порядке, вы можете закрыть ее, в противном случае вы можете удалить ее.

Когда вы закроете его, в нижней части вы можете проверить, успешно ли он был закрыт, нажав на вкладку “Активность”. Вы должны увидеть что-то похожее на это:

Если репозиторий был успешно закрыт, теперь вы можете продвинуть его до выпуска.

Выполните следующую команду, используя свой идентификатор хранилища. На изображении выше это com отличная демонстрационная библиотека api-1005 . Не забудьте заменить его своим собственным идентификатором .

mvn nexus-постановка: выпуск – 1005

Используйте свой собственный идентификатор группы. Если вы используете библиотеку com.the great.api.demo вы не сможете опубликовать в Maven Central.

Вы должны увидеть результат, похожий на:

Теперь ваша библиотека опубликована. В этом самом первом развертывании вы должны прокомментировать билет JIRA, чтобы они могли активировать синхронизацию с Maven Central.

После того, как Sonatype активирует центральную синхронизацию Maven для вашей библиотеки, когда вы успешно выпустите новые компоненты, они будут опубликованы в Central https://repo1.maven.org/maven2/ , как правило, в течение 10 минут, хотя обновления https://search.maven.org может занять до двух часов.

Начните Свою Библиотеку Прямо Сейчас

Теперь вы готовы создать свою собственную библиотеку. Это будет здорово для вас и сообщества Java.

Если вы все еще не чувствуете себя готовым, что мешает вам сделать следующий шаг? Прокомментируйте здесь или отправьте мне сообщение в мои социальные сети . Я буду рад вам помочь.

Как создать библиотеку?

У меня есть три «куска» кода — интерфейс, абстрактный класс и класс. Класс наследует абстрактный класс, а абстрактный класс наследует интерфейс. Класс переопределяет и реализует все методы из интерфейса и Абстрактного Класса. Я хочу сделать из них библиотеку, а точнее пока только jar файл, чтобы импортировать их в свой код. Нужно ли мне делать их в разных проектах, но в одной раскладке(package)? Если да, то как мне их из разных проектов запилить в один jar-файл?

Или вообще можно не писать интерфейс и абстрактный класс, а оставить просто класс?

Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

1

Создание и использование библиотеки

17.07.2011, 15:55. Показов 26557. Ответов 26


Всем привет.
Интересует вопрос: как правильно создать библиотеку, в которой будут находиться классы, различные иконки например, а затем подключать в любое Java приложение.

Пока что пробовал запихать все в jar файл, а именно .class файлы классов, пиктограммы.
Работая в NetBeans подключил этот файл в Libraries, и использовал их посредством рефлексии.

Как правильно создать библиотеку, где будут находиться .java, .class и тд, и без проблем использовать ее?
И будет ли возможность в редакторе форм netbeans создать объект класса из библиотеки, путем перетаскивания на форму?
Спасибо.



0



81 / 81 / 8

Регистрация: 10.06.2011

Сообщений: 258

18.07.2011, 02:23

2

А причём тут reflection?
Просто пишеш проект который тебе нужен, экспортируеш его в jar и «библиотека» готова. Потом эту библиотеку нужно прописать в classpath и можеш работать с классами, которые находятся в ней. Как это конкретно сделать в NetBeans почитай в документации, ну или в гугле по «netbeans import jar»

Так же в jar можно поместить и исходники, если это зачем-то нужно.



1



Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 12:36

 [ТС]

3

То есть вы хотите сказать запихать .class файлы в jar?
Но откуда тогда IDE будет знать как работать с классом не имея исходного кода?
Я такие классы использовал при помощи рефлексии.
Я вот и хочу узнать, вроде как-то создаются библиотеки где .java и .class связываются между собой.



0



81 / 81 / 8

Регистрация: 10.06.2011

Сообщений: 258

18.07.2011, 12:42

4

IDE уже в свою очередь где то в глубине использует reflection. java и class файлы «связать» не возможно, потому то там нечего связывать.



0



Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 13:02

 [ТС]

5

Да не получается использовать .class до компиляции, тока при помощи рефлексии.
Может я чего не понимаю, покажите пожалуйста как имея один .class файл класса, без рефлексии использовать его.



0



81 / 81 / 8

Регистрация: 10.06.2011

Сообщений: 258

18.07.2011, 13:14

6

Что конкретно занчит «не получается использовать»?
Думаю, что просто неправильно настроен classpath: в IDE библиотека не присутсвует, а при запуске программы она прописывается в путь.
Допустим есть файл «lib.jar». Какие конкретные действия ты делаеш, что бы её использовать?



0



John_Pa9JIbHuK

Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 13:28

 [ТС]

7

Допустим в lib.jar есть единственный пакет JavaPac и в нем единственный файл MyClass.class.
1) Добавляю эту библиотеку (этот jar файл) в раздел libraries (в проекте NetBeans есть такой раздел в проекте, помоему как и в eclipse).
2) Теперь использую:

Java
1
2
3
4
5
Class A = Class.forName("JavaPac.MyClass");
...
...
...
...

Все проходит без ошибок.
Ну а дальше уже использую.

Но это очень не удобно, вызывать методы и тд.

Можете показать как вы создаете библиотеку, и по подробнее рассказать про classpath?



0



XHelp

81 / 81 / 8

Регистрация: 10.06.2011

Сообщений: 258

18.07.2011, 13:30

8

Ну а если просто:

Java
1
2
3
4
5
6
import JavaPac.MyClass;
public class Test {
 ...
  MyClass testInstance = new MyClass();
 ...
}

? Должно сработать



0



Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 13:32

 [ТС]

9

Вот пример подобной библиотеки:



0



XHelp

81 / 81 / 8

Регистрация: 10.06.2011

Сообщений: 258

18.07.2011, 13:50

10

Всё еще вопрос: какие конкретно jar-файлы подключаются? Надеюсь не просто переименовывается zip в jar?
Что пишет IDE, если попробовать скомпилировать следующий класс:

Java
1
2
3
4
5
6
7
import com.googleapis.ajax.common.PagedArrayList;
 
public class Tester {
    public static void main(String[] args) {
        PagedArrayList<Object> test = new PagedArrayList<Object>();
    }
}



0



Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 14:36

 [ТС]

11

import com.googleapis.ajax.common.PagedArrayList;
Пишет что не найдено

Добавлено через 9 минут
Class A = Class.forName(«com.googleapis.ajax.common.PagedArr ayList»);
Также выбросило ClassNotFoundException



0



81 / 81 / 8

Регистрация: 10.06.2011

Сообщений: 258

18.07.2011, 14:49

12

Так а что конкретно подключается как библиотека?



0



Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 14:52

 [ТС]

13

Класс он нашел.
Но почему то прямо из zip он не смог его подключить.
Как быть дальше?
Как все же должна быть сгруппирована библиотека?
Начнем например с того что я создам пакет MyPac и класс MyClass.java.

Добавлено через 2 минуты
XHelp,
Я попробовал сначал подключить .zip, NetBeans отобразил иерархию, даже выдавал подсказки в коде com.googleapis…. но в и тоге сообщал что не может найти эти пакеты.
Потом извлек все в папку, затем все заработало



0



81 / 81 / 8

Регистрация: 10.06.2011

Сообщений: 258

18.07.2011, 14:57

14

.zip подгружать во-первый не надо, а во-вторых нельзя. В проекте уже есть собраные maven’ом .jar (в target папках). Их и нужно подключать.
Насчёт своей собственной библиотеки и NetBeans:
я не работал с ним, поэтому информация на быструю руку нагуглена:
— Создаётся проект, пишется всё что надо.
— проверяется, стоит ли опция: Project Properties -> Build -> Packaging -> Build JAR after compiling
— делается clean and build
— в папке проекта dist лежит готовый .jar, который и является «библиотекой»



1



Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 15:26

 [ТС]

15

смотрите:
Я извлеку googleapis-ajax-schema-0.1.jar из папки schematarget этого jar будет достаточно?
В этом jar находятся .class файлы.

Добавлено через 2 минуты
XHelp, Не совсем меня поняли, я создал jar файл в NetBeanse, я имел ввиду что в нем находятся ведь только class файлы, а их недостаточно, нужны ведь и исходники, скажем если я хочу дать свою библиотеку кому либо, одного jar будет ведь недостаточно. Я прав?

Добавлено через 13 минут
1) JAR файл с class файлами — с этим все ясно. Одного его может хватить если использовать рефлексию.
2) Вопрос в том, как сделать взаимосвязь между .java и .class файлами, чтобы в библиотеке были как исходники так и class файлы.
3) Я не понял суть classpath, можете рассказать суть.

Спасибо.



0



81 / 81 / 8

Регистрация: 10.06.2011

Сообщений: 258

18.07.2011, 15:36

16

Цитата
Сообщение от John_Pa9JIbHuK
Посмотреть сообщение

смотрите:
Я извлеку googleapis-ajax-schema-0.1.jar из папки schematarget этого jar будет достаточно?

Судя по всему — нет. Нужно брать distgoogle-ajax-apis.jar ну или disttargetgoogleapis-ajax-release-0.1-release.jar . Для этой библиотеки где-нибудь наверно есть документация, которую следовало бы почитать

XHelp, Не совсем меня поняли, я создал jar файл в NetBeanse, я имел ввиду что в нем находятся ведь только class файлы, а их недостаточно, нужны ведь и исходники, скажем если я хочу дать свою библиотеку кому либо, одного jar будет ведь недостаточно. Я прав?

Нет. Исходники абсолютно никакого отношения к использованию библиотеки не имеют. Это я уже говорил чуть раньше. Что бы было понятно как она работает нужно javadoc’s писать, а не исходники прилогать.

1) JAR файл с class файлами — с этим все ясно. Одного его может хватить если использовать рефлексию.

Рефлексия тут не причём. Об этом тоже шла речь выше.

2) Вопрос в том, как сделать взаимосвязь между .java и .class файлами, чтобы в библиотеке были как исходники так и class файлы.

Взаимосвязи нету как таковой. Можно исходник туда поместить, но зачем?

3) Я не понял суть classpath, можете рассказать суть.

В classpath (как и говорит имя) указываются пути, где будут искаться классы. Тоесть нужно использовать что-то внешнее, то нужно его прописывать. IDE всю эту работы берут на себя.
Подробнее в документации: http://download.oracle.com/jav… spath.html



1



John_Pa9JIbHuK

Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 16:07

 [ТС]

17

Из ваших слов я понимаю что бы воспользоваться написанным мной кодом, достаточно будет создать jar файл где будут одни тока class файлы, без исходников.
Затем подключив этот jar к проекту мы сможем использовать классы оттуда, я прально понял?
Тогда:
Создадим библиотеку MyClassLib
В ней создадим пакет MyPackage
В этом пакете создадим класс MyClass
В этом классе создадим метод MyMethod
в итоге имеем

Java
1
2
3
4
5
6
7
8
package MyPackage;
 
 
public class MyClass {
    public void myMethod(){
        System.out.println("It's my method");
    }
}

Все библиотека готова, теперь в ней MyPackage — >MyClass.class
Теперь удаляем исходные коды которые мы писали а именно MyClass.java

Далее спокойно подключаем ее к проекту, готово.
Теперь пробуем подключить и использовать — ошибка. Если подключить jar, но MyClass.java не удалять, то как по маслу



0



81 / 81 / 8

Регистрация: 10.06.2011

Сообщений: 258

18.07.2011, 16:11

18

Что значит «удаляем исходные коды»? Их там и так нету. Как создаётся jar?



0



Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 16:27

 [ТС]

19

Упаковываю в jar из NetBeans
В папке disk создается jar файл
В папке src исходники.
Если из src удалить исходники, то все перестает работать.

Изображения

 



0



John_Pa9JIbHuK

Добрый самаритянин

1107 / 622 / 139

Регистрация: 31.03.2009

Сообщений: 2,567

18.07.2011, 16:36

 [ТС]

20

Даже это не работает без .java

Java
1
2
3
4
5
6
7
8
9
10
    public static void main(String[] args){
        try {
            Class C = Class.forName("MyPackage.MyClass");
            Method M = C.getMethod("myMethod");
            Object ob = C.newInstance();
            M.invoke(ob);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

В чем дело? Оо

Добавлено через 33 секунды
Выбрасывает classNotFoundEx



0



Понравилась статья? Поделить с друзьями:
  • Как написать свою ide
  • Как написать свою cms
  • Как написать свою cad программу
  • Как написать свойства параболы
  • Как написать свой язык программирования на ассемблере